aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libreboot/patch')
-rw-r--r--resources/libreboot/patch/kgpe-d16/0001-util-cbmem-Fix-failure-with-certain-cbmem-base-align.patch205
-rw-r--r--resources/libreboot/patch/kgpe-d16/0002-cpu-amd-microcode-Update-microcode-parser-to-handle-.patch41
-rw-r--r--resources/libreboot/patch/kgpe-d16/0003-arch-x86-boot-smbios-Add-SPD-IDs-for-Kingston-and-Co.patch36
-rw-r--r--resources/libreboot/patch/kgpe-d16/0004-arch-x86-smbios-Add-Crucial-DIMM-manufacturer-ID.patch27
-rw-r--r--resources/libreboot/patch/kgpe-d16/0005-southbridge-amd-sr5650-Remove-unnecessary-register-c.patch33
-rw-r--r--resources/libreboot/patch/kgpe-d16/0006-drivers-i2c-w83795-Add-full-support-for-fan-control-.patch661
-rw-r--r--resources/libreboot/patch/kgpe-d16/0007-drivers-aspeed-Add-native-text-mode-VGA-support-for-.patch3673
-rw-r--r--resources/libreboot/patch/kgpe-d16/0008-southbridge-amd-sb700-Fix-boot-hang-on-ASUS-KGPE-D16.patch641
-rw-r--r--resources/libreboot/patch/kgpe-d16/0009-southbridge-amd-sr5650-Fix-boot-failure-on-ASUS-KGPE.patch619
-rw-r--r--resources/libreboot/patch/kgpe-d16/0010-cpu-amd-Add-initial-support-for-AMD-Socket-G34-proce.patch788
-rw-r--r--resources/libreboot/patch/kgpe-d16/0011-northbridge-amd-amdmct-Fix-broken-AMD-K10-DDR3-memor.patch3451
-rw-r--r--resources/libreboot/patch/kgpe-d16/0012-northbridge-amd-amdmct-mct_ddr3-Fix-curly-brace-styl.patch93
-rw-r--r--resources/libreboot/patch/kgpe-d16/0013-northbridge-amd-amdfam10-Limit-maximum-RAM-clock-to-.patch120
-rw-r--r--resources/libreboot/patch/kgpe-d16/0014-northbridge-amd-amdfam10-Fix-typo-in-comment.patch33
-rw-r--r--resources/libreboot/patch/kgpe-d16/0015-device-hypertransport-Add-additional-debug-output.patch33
-rw-r--r--resources/libreboot/patch/kgpe-d16/0016-mainboard-asus-kgpe-d16-Add-initial-support-for-the-.patch3218
-rw-r--r--resources/libreboot/patch/kgpe-d16/0017-mainboard-asus-kgpe-d16-Add-nvram-option-to-enable-d.patch70
-rw-r--r--resources/libreboot/patch/kgpe-d16/0018-cpu-amd-model_10xxx-Clean-up-debugging-statements.patch114
-rw-r--r--resources/libreboot/patch/kgpe-d16/0019-southbridge-amd-sb700-Add-Suspend-to-RAM-S3-support.patch365
-rw-r--r--resources/libreboot/patch/kgpe-d16/0020-superio-nuvoton-nct5572d-Enable-power-state-after-po.patch80
-rw-r--r--resources/libreboot/patch/kgpe-d16/0021-northbridge-amd-amdfam10-Add-Suspend-to-RAM-S3-Flash.patch156
-rw-r--r--resources/libreboot/patch/kgpe-d16/0022-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch986
-rw-r--r--resources/libreboot/patch/kgpe-d16/0023-cpu-amd-car-Add-initial-Suspend-to-RAM-S3-support.patch54
-rw-r--r--resources/libreboot/patch/kgpe-d16/0024-mainboard-asus-kgpe-d16-Add-initial-Suspend-to-RAM-S.patch830
-rw-r--r--resources/libreboot/patch/kgpe-d16/0025-include-smbios-Update-SMBIOS-memory-structures-to-ve.patch31
-rw-r--r--resources/libreboot/patch/kgpe-d16/0026-mainboard-asus-kgpe-d16-Set-DDR3-memory-voltage-base.patch151
-rw-r--r--resources/libreboot/patch/kgpe-d16/0027-northbridge-amd-amdfam10-Set-DIMM-voltage-based-on-S.patch176
-rw-r--r--resources/libreboot/patch/kgpe-d16/0028-src-console-Add-x86-romstage-spinlock-option.patch98
-rw-r--r--resources/libreboot/patch/kgpe-d16/0029-northbridge-amd-amdmct-mct_ddr3-Fix-S3-suspend-overr.patch49
-rw-r--r--resources/libreboot/patch/kgpe-d16/0030-northbridge-amd-amdmct-mct_ddr3-Fix-failing-S3-resum.patch46
-rw-r--r--resources/libreboot/patch/kgpe-d16/0031-northbridge-amd-amdmct-Fix-S3-suspend-resume-with-la.patch78
-rw-r--r--resources/libreboot/patch/kgpe-d16/0032-src-console-Add-x86-printk-spinlock-support.patch126
-rw-r--r--resources/libreboot/patch/kgpe-d16/0033-lib-stack-Add-stack-overrun-detection.patch36
-rw-r--r--resources/libreboot/patch/kgpe-d16/0034-cpu-x86-lapic-Add-stack-overrun-detection.patch32
-rw-r--r--resources/libreboot/patch/kgpe-d16/0035-southbridge-amd-sr5650-Add-AMD-Family-15h-CPU-suppor.patch26
-rw-r--r--resources/libreboot/patch/kgpe-d16/0036-cpu-amd-Add-initial-AMD-Family-15h-support.patch15930
-rw-r--r--resources/libreboot/patch/kgpe-d16/0037-mainboard-asus-kgpe-d16-Add-initial-Family-15h-CPU-s.patch563
-rw-r--r--resources/libreboot/patch/kgpe-d16/0038-amdmct-mct_ddr3-Disable-Fam10h-specific-MTRR-setup-o.patch36
-rw-r--r--resources/libreboot/patch/kgpe-d16/0039-cpu-amd-car-Add-romstage-BSP-stack-overrun-detection.patch57
-rw-r--r--resources/libreboot/patch/kgpe-d16/0040-cpu-amd-car-Increase-Family-10h-CAR-size-limit-to-12.patch28
-rw-r--r--resources/libreboot/patch/kgpe-d16/0041-cpu-amd-car-Move-AP-stacks-below-the-BSP-stack-to-fr.patch30
-rw-r--r--resources/libreboot/patch/kgpe-d16/0042-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch459
-rw-r--r--resources/libreboot/patch/kgpe-d16/0043-cpu-amd-car-Initialize-entire-CAR-space-instead-of-o.patch32
-rw-r--r--resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Improve-SPD-DIMM-detect-reliabil.patch47
-rw-r--r--resources/libreboot/patch/kgpe-d16/0045-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch769
-rw-r--r--resources/libreboot/patch/kgpe-d16/0046-northbridge-amd-amdfam10-Enable-CC6-DRAM-save-area-s.patch259
-rw-r--r--resources/libreboot/patch/kgpe-d16/0047-mainboard-asus-kgpe-d16-Enable-CC6.patch68
-rw-r--r--resources/libreboot/patch/kgpe-d16/0048-cpu-amd-Add-CC6-support.patch1302
-rw-r--r--resources/libreboot/patch/kgpe-d16/0049-northbridge-amd-amdmct-Skip-DCT-config-write-to-Flas.patch101
-rw-r--r--resources/libreboot/patch/kgpe-d16/0050-southbridge-amd-sb700-Add-AHCI-support.patch658
-rw-r--r--resources/libreboot/patch/kgpe-d16/0051-mainboard-asus-kgpe-d16-Properly-initialize-SB700-SA.patch47
-rw-r--r--resources/libreboot/patch/kgpe-d16/0052-southbridge-amd-sb700-Disable-broken-SATA-MSI-functi.patch39
-rw-r--r--resources/libreboot/patch/kgpe-d16/0053-southbridge-amd-sb700-Indicate-iSATA-eSATA-port-type.patch92
-rw-r--r--resources/libreboot/patch/kgpe-d16/0054-northbridge-amd-amdfam10-Add-ability-to-set-maximum-.patch90
-rw-r--r--resources/libreboot/patch/kgpe-d16/0055-northbridge-amd-amdmct-Verify-MCT-NVRAM-options-befo.patch143
-rw-r--r--resources/libreboot/patch/kgpe-d16/0056-src-northbridge-amd-amdmct-Add-option-to-override-ba.patch106
-rw-r--r--resources/libreboot/patch/kgpe-d16/0057-mainboard-asus-kgpe-d16-Add-missing-IRQ-routing-for-.patch207
-rw-r--r--resources/libreboot/patch/kgpe-d16/0058-northbridge-amd-amdmct-Fix-hang-on-boot-due-to-inval.patch35
-rw-r--r--resources/libreboot/patch/kgpe-d16/0059-southbridge-amd-sr5650-Fix-GPP3a-link-training-in-hi.patch83
-rw-r--r--resources/libreboot/patch/kgpe-d16/0060-southbridge-amd-sr5650-Add-optional-delay-after-link.patch68
-rw-r--r--resources/libreboot/patch/kgpe-d16/0061-mainboard-asus-kgpe-d16-Properly-configure-SR5690-so.patch30
-rw-r--r--resources/libreboot/patch/kgpe-d16/0062-southbridge-amd-sb700-Add-option-to-disable-SATA-ALP.patch84
-rw-r--r--resources/libreboot/patch/kgpe-d16/0063-mainboard-asus-kgpe-d16-Set-SP5100-subtype.patch24
-rw-r--r--resources/libreboot/patch/kgpe-d16/0064-northbridge-amd-amdmct-Fix-crash-on-startup-due-to-N.patch31
-rw-r--r--resources/libreboot/patch/kgpe-d16/0065-northbridge-amd-amdmct-Clear-memory-before-enabling-.patch183
-rw-r--r--resources/libreboot/patch/kgpe-d16/0066-southbridge-amd-sb700-Do-drive-detection-even-in-AHC.patch140
-rw-r--r--resources/libreboot/patch/kgpe-d16/0067-src-southbridge-amd-sb700-Reset-SATA-controller-in-A.patch93
-rw-r--r--resources/libreboot/patch/kgpe-d16/0068-southbridge-amd-sb700-Recover-if-AHCI-disk-detection.patch161
-rw-r--r--resources/libreboot/patch/kgpe-d16/0069-southbridge-amd-sb700-Fix-SATA-port-4-5-drive-detect.patch95
-rw-r--r--resources/libreboot/patch/kgpe-d16/0070-southbridge-amd-sb700-Fix-random-persistent-SATA-AHC.patch163
-rw-r--r--resources/libreboot/patch/kgpe-d16/0071-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch423
-rw-r--r--resources/libreboot/patch/kgpe-d16/0072-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch479
-rw-r--r--resources/libreboot/patch/kgpe-d16/0073-northbridge-amd-amdfam10-Properly-indicate-node-and-.patch75
-rw-r--r--resources/libreboot/patch/kgpe-d16/0074-amd-amdmct-mct_ddr3-Add-Family-15h-RDIMM-timing-and-.patch513
-rw-r--r--resources/libreboot/patch/kgpe-d16/0075-northbridge-amd-amdmct-mct_ddr3-Attempt-to-recover-f.patch254
-rw-r--r--resources/libreboot/patch/kgpe-d16/0076-northbridge-amd-amdmct-mct_ddr3-Work-around-strange-.patch40
-rw-r--r--resources/libreboot/patch/kgpe-d16/0077-northbridge-amd-amdmct-mct_ddr3-Add-additional-debug.patch118
-rw-r--r--resources/libreboot/patch/kgpe-d16/0078-northbridge-amd-amdmct-mct_ddr3-Fix-null-pointer-acc.patch238
-rw-r--r--resources/libreboot/patch/kgpe-d16/0079-northbridge-amd-amdmct-mct_ddr3-Add-missing-Family-1.patch266
-rw-r--r--resources/libreboot/patch/kgpe-d16/0080-northbridge-amd-amdmct-mct_ddr3-Set-SkewMemClk-when-.patch112
-rw-r--r--resources/libreboot/patch/kgpe-d16/0081-northbridge-amd-amdmct-mct_ddr3-Properly-indicate-cl.patch56
-rw-r--r--resources/libreboot/patch/kgpe-d16/0082-northbridge-amd-amdmct-mct_ddr3-Fix-Family-10h-boot-.patch98
-rw-r--r--resources/libreboot/patch/kgpe-d16/0083-src-southbridge-amd-sr5650-Always-configure-lane-dir.patch48
-rw-r--r--resources/libreboot/patch/kgpe-d16/0084-cpu-amd-model_10xxx-Fix-BSP-stack-corruption-on-32-c.patch26
-rw-r--r--resources/libreboot/patch/kgpe-d16/0085-northbridge-amd-amdmct-mct_ddr3-Fix-RDIMM-errors-due.patch411
-rw-r--r--resources/libreboot/patch/kgpe-d16/0086-amd-amdmct-mct_ddr3-Partially-fix-up-registered-DIMM.patch957
-rw-r--r--resources/libreboot/patch/kgpe-d16/0087-northbridge-amd-amdmct-Fix-Family-15h-detection.patch47
-rw-r--r--resources/libreboot/patch/kgpe-d16/0088-northbridge-amd-amdmct-mct_ddr3-Add-registered-and-x.patch1893
-rw-r--r--resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch948
-rw-r--r--resources/libreboot/patch/kgpe-d16/0090-northbridge-amd-amdfam10-Add-probe-filter-support.patch212
-rw-r--r--resources/libreboot/patch/kgpe-d16/0091-cpu-amd-model_10xxx-Bring-initial-HT-register-config.patch246
-rw-r--r--resources/libreboot/patch/kgpe-d16/0092-northbridge-amd-amdmct-mct_ddr3-Move-K10D-configurat.patch334
-rw-r--r--resources/libreboot/patch/kgpe-d16/0093-mainboard-asus-kgpe-d16-Fix-I-O-link-detection.patch26
-rw-r--r--resources/libreboot/patch/kgpe-d16/0094-cpu-amd-model_10xxx-Set-northbridge-throttle-values.patch136
-rw-r--r--resources/libreboot/patch/kgpe-d16/0095-cpu-amd-model_10xxx-Fix-incorrect-revision-detection.patch62
-rw-r--r--resources/libreboot/patch/kgpe-d16/0096-northbridge-amd-amdht-Add-support-for-HT3-2.8GHz-and.patch559
-rw-r--r--resources/libreboot/patch/kgpe-d16/0097-amd-model_10xxx-Fix-poor-performance-on-Family-15h-C.patch122
-rw-r--r--resources/libreboot/patch/kgpe-d16/0098-amd-amdmct-mct_ddr3-Fix-poor-performance-on-Family-1.patch1087
-rw-r--r--resources/libreboot/patch/kgpe-d16/0099-northbridge-amd-amdht-Fix-poor-performance-on-Family.patch28
-rw-r--r--resources/libreboot/patch/kgpe-d16/0100-northbridge-amd-amdfam10-Fix-poor-performance-on-Fam.patch70
-rw-r--r--resources/libreboot/patch/kgpe-d16/0101-cpu-amd-model_10xxx-Configure-NB-register-2.patch31
-rw-r--r--resources/libreboot/patch/kgpe-d16/0102-cpu-amd-model_10xxx-Set-up-link-XCS-token-counts-on-.patch342
-rw-r--r--resources/libreboot/patch/kgpe-d16/0103-northbridge-amd-amdmct-mct_ddr3-Force-retraining-on-.patch34
-rw-r--r--resources/libreboot/patch/kgpe-d16/0104-northbridge-amd-amdfam10-Fix-invalid-NUMA-table.patch26
-rw-r--r--resources/libreboot/patch/kgpe-d16/0105-northbridge-amd-amdfam10-Add-Family-15h-cache-partit.patch121
-rw-r--r--resources/libreboot/patch/kgpe-d16/0106-amd-amdmct-mct_ddr3-Set-prefetch-double-stride-to-im.patch25
-rw-r--r--resources/libreboot/patch/kgpe-d16/0107-cpu-amd-model_10xxx-Set-up-Family-15h-Link-Base-Chan.patch190
-rw-r--r--resources/libreboot/patch/kgpe-d16/0108-cpu-amd-model_10xxx-Set-up-cache-controls-on-Family-.patch41
-rw-r--r--resources/libreboot/patch/kgpe-d16/0109-cpu-amd-model_10xxx-Set-up-SRI-to-XCS-Token-Count-re.patch64
-rw-r--r--resources/libreboot/patch/kgpe-d16/0110-amd-amdfam10-Control-Family-15h-cache-partitioning-a.patch175
-rw-r--r--resources/libreboot/patch/kgpe-d16/0111-northbridge-amd-amdht-Add-isochronous-setup-support-.patch270
-rw-r--r--resources/libreboot/patch/kgpe-d16/0112-arch-x86-acpi-Add-IVRS-table-generation-routines.patch119
-rw-r--r--resources/libreboot/patch/kgpe-d16/0113-southbridge-amd-sr5650-Add-IOMMU-support.patch709
-rw-r--r--resources/libreboot/patch/kgpe-d16/0114-southbridge-amd-sr5650-Hide-clock-configuration-devi.patch55
-rw-r--r--resources/libreboot/patch/kgpe-d16/0115-northbridge-amd-amdfam10-Rename-mislabeled-iommu-nvr.patch53
-rw-r--r--resources/libreboot/patch/kgpe-d16/0116-northbridge-amd-amdfam10-Fix-gart-setup-not-working-.patch94
-rw-r--r--resources/libreboot/patch/kgpe-d16/0117-mainboard-asus-kgpe-d16-Add-several-nvram-configurat.patch127
-rw-r--r--resources/libreboot/patch/kgpe-d16/0118-southbridge-amd-sr5650-Use-correct-PCI-configuration.patch28
-rw-r--r--resources/libreboot/patch/kgpe-d16/0119-southbridge-amd-sr5650-Add-MCFG-ACPI-table-support.patch83
-rw-r--r--resources/libreboot/patch/kgpe-d16/0120-southbridge-amd-sb700-Fix-mismatched-FADT-entries.patch34
-rw-r--r--resources/libreboot/patch/kgpe-d16/0121-southbridge-amd-sb700-Fix-drifting-system-clock.patch45
-rw-r--r--resources/libreboot/patch/kgpe-d16/0122-northbridge-amd-amdmct-mct_ddr3-Add-cc6-setup-inform.patch56
-rw-r--r--resources/libreboot/patch/kgpe-d16/0123-northbridge-amd-amdfam10-Work-around-sporadic-lockup.patch38
-rw-r--r--resources/libreboot/patch/kgpe-d16/0124-northbridge-amd-amdmct-mct_ddr3-Ensure-channel-clock.patch113
-rw-r--r--resources/libreboot/patch/kgpe-d16/0125-northbridge-amd-amdmct-mct_ddr3-Add-DDR3-termination.patch34
-rw-r--r--resources/libreboot/patch/kgpe-d16/0126-northbridge-amd-amdmct-mct_ddr3-Fix-a-minor-RDIMM-CS.patch35
-rw-r--r--resources/libreboot/patch/kgpe-d16/0127-northbridge-amd-amdmct-mct_ddr3-Fix-odd-rank-data-co.patch63
-rw-r--r--resources/libreboot/patch/kgpe-d16/0128-northbridge-amd-amdmct-mct_ddr3-Use-antiphase-to-bet.patch156
-rw-r--r--resources/libreboot/patch/kgpe-d16/0129-northbridge-amd-amdmct-mct_ddr3-Fix-broken-support-f.patch602
-rw-r--r--resources/libreboot/patch/kgpe-d16/0130-drivers-pc80-Add-optional-spinlock-for-nvram-CBFS-ac.patch172
-rw-r--r--resources/libreboot/patch/kgpe-d16/0131-mainboard-asus-kgpe-d16-Enable-CBFS-spinlocks.patch66
-rw-r--r--resources/libreboot/patch/kgpe-d16/0132-cpu-amd-microcode-Introduce-CBFS-access-spinlock-to-.patch151
-rw-r--r--resources/libreboot/patch/kgpe-d16/0133-mainboard-asus-kgpe-d16-Limit-HT-speed-to-2.6GHz.patch38
-rw-r--r--resources/libreboot/patch/kgpe-d16/0134-cpu-amd-model_10xxx-Apply-missing-Family-15h-errata-.patch68
-rw-r--r--resources/libreboot/patch/kgpe-d16/0135-northbridge-amd-amdmct-mct_ddr3-Use-StopOnError-to-d.patch229
-rw-r--r--resources/libreboot/patch/kgpe-d16/0136-mainboard-asus-kgpe-d16-Enable-GART-by-default.patch25
-rw-r--r--resources/libreboot/patch/kgpe-d16/0137-northbridge-amd-amdfam10-Fix-incorrect-channel-buffe.patch40
-rw-r--r--resources/libreboot/patch/kgpe-d16/0138-cpu-amd-model_10xxx-Force-iolink-detect-to-either-1-.patch35
-rw-r--r--resources/libreboot/patch/kgpe-d16/0139-northbridge-amd-amdht-Fix-XCS-buffer-count-setup-on-.patch142
-rw-r--r--resources/libreboot/patch/kgpe-d16/0140-cpu-amd-model_10xxx-Fix-link-type-detection-and-XCS-.patch156
-rw-r--r--resources/libreboot/patch/kgpe-d16/0141-cpu-amd-model_10xxx-Enable-DFE-on-Family-15h-HT3-lin.patch44
141 files changed, 54242 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0001-util-cbmem-Fix-failure-with-certain-cbmem-base-align.patch b/resources/libreboot/patch/kgpe-d16/0001-util-cbmem-Fix-failure-with-certain-cbmem-base-align.patch
new file mode 100644
index 00000000..84c51ba1
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0001-util-cbmem-Fix-failure-with-certain-cbmem-base-align.patch
@@ -0,0 +1,205 @@
+From 4e0a69562a189e9abe06979a993f50e3f0b2069b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:07:17 -0500
+Subject: [PATCH 001/146] util/cbmem: Fix failure with certain cbmem base
+ alignments
+
+---
+ util/cbmem/cbmem.c | 61 ++++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 38 insertions(+), 23 deletions(-)
+
+diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c
+index 74cb52d..69ffbaf 100644
+--- a/util/cbmem/cbmem.c
++++ b/util/cbmem/cbmem.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2012 Google Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -103,8 +104,7 @@ static void unmap_memory(void)
+ if (size_to_mib(mapped_size) == 0) {
+ debug("Unmapping %zuMB of virtual memory at %p.\n",
+ size_to_mib(mapped_size), mapped_virtual);
+- }
+- else {
++ } else {
+ debug("Unmapping %zuMB of virtual memory at %p.\n",
+ size_to_mib(mapped_size), mapped_virtual);
+ }
+@@ -113,7 +113,7 @@ static void unmap_memory(void)
+ mapped_size = 0;
+ }
+
+-static void *map_memory_size(u64 physical, size_t size)
++static void *map_memory_size(u64 physical, size_t size, uint8_t abort_on_failure)
+ {
+ void *v;
+ off_t p;
+@@ -131,8 +131,7 @@ static void *map_memory_size(u64 physical, size_t size)
+ if (size_to_mib(size) == 0) {
+ debug("Mapping %zuB of physical memory at 0x%jx (requested 0x%jx).\n",
+ size, (intmax_t)p, (intmax_t)physical);
+- }
+- else {
++ } else {
+ debug("Mapping %zuMB of physical memory at 0x%jx (requested 0x%jx).\n",
+ size_to_mib(size), (intmax_t)p, (intmax_t)physical);
+ }
+@@ -153,9 +152,13 @@ static void *map_memory_size(u64 physical, size_t size)
+ }
+
+ if (v == MAP_FAILED) {
+- fprintf(stderr, "Failed to mmap /dev/mem: %s\n",
+- strerror(errno));
+- exit(1);
++ if (abort_on_failure) {
++ fprintf(stderr, "Failed to mmap /dev/mem: %s\n",
++ strerror(errno));
++ exit(1);
++ } else {
++ return 0;
++ }
+ }
+
+ /* Remember what we actually mapped ... */
+@@ -173,7 +176,7 @@ static void *map_memory_size(u64 physical, size_t size)
+
+ static void *map_memory(u64 physical)
+ {
+- return map_memory_size(physical, MAP_BYTES);
++ return map_memory_size(physical, MAP_BYTES, 1);
+ }
+
+ /*
+@@ -210,14 +213,16 @@ static struct lb_cbmem_ref parse_cbmem_ref(struct lb_cbmem_ref *cbmem_ref)
+ return ret;
+ }
+
+-static int parse_cbtable(u64 address, size_t table_size)
++static int parse_cbtable(u64 address, size_t table_size, uint8_t abort_on_failure)
+ {
+- int i, found = 0;
++ int i, found, ret = 0;
+ void *buf;
+
+ debug("Looking for coreboot table at %" PRIx64 " %zd bytes.\n",
+ address, table_size);
+- buf = map_memory_size(address, table_size);
++ buf = map_memory_size(address, table_size, abort_on_failure);
++ if (!buf)
++ return -2;
+
+ /* look at every 16 bytes within 4K of the base */
+
+@@ -283,7 +288,17 @@ static int parse_cbtable(u64 address, size_t table_size)
+ *(struct lb_forward *) lbr_p;
+ debug(" Found forwarding entry.\n");
+ unmap_memory();
+- return parse_cbtable(lbf_p.forward, table_size);
++ ret = parse_cbtable(lbf_p.forward, table_size, 0);
++ if (ret == -2) {
++ /* try again with a smaller memory mapping request */
++ ret = parse_cbtable(lbf_p.forward, table_size / 2, 1);
++ if (ret == -2)
++ exit(1);
++ else
++ return ret;
++ } else {
++ return ret;
++ }
+ }
+ default:
+ break;
+@@ -544,7 +559,7 @@ static void dump_timestamps(int mach_readable)
+ }
+
+ size = sizeof(*tst_p);
+- tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size);
++ tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size, 1);
+
+ timestamp_set_tick_freq(tst_p->tick_freq_mhz);
+
+@@ -553,7 +568,7 @@ static void dump_timestamps(int mach_readable)
+ size += tst_p->num_entries * sizeof(tst_p->entries[0]);
+
+ unmap_memory();
+- tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size);
++ tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size, 1);
+
+ /* Report the base time within the table. */
+ prev_stamp = 0;
+@@ -604,7 +619,7 @@ static void dump_console(void)
+ }
+
+ console_p = map_memory_size((unsigned long)console.cbmem_addr,
+- 2 * sizeof(uint32_t));
++ 2 * sizeof(uint32_t), 1);
+ /* The in-memory format of the console area is:
+ * u32 size
+ * u32 cursor
+@@ -626,7 +641,7 @@ static void dump_console(void)
+ }
+
+ console_p = map_memory_size((unsigned long)console.cbmem_addr,
+- size + sizeof(size) + sizeof(cursor));
++ size + sizeof(size) + sizeof(cursor), 1);
+ memcpy(console_c, console_p + 8, size);
+ console_c[size] = 0;
+ console_c[cursor] = 0;
+@@ -647,7 +662,7 @@ static void hexdump(unsigned long memory, int length)
+ uint8_t *m;
+ int all_zero = 0;
+
+- m = map_memory_size((intptr_t)memory, length);
++ m = map_memory_size((intptr_t)memory, length, 1);
+
+ if (length > MAP_BYTES) {
+ printf("Truncating hex dump from %d to %d bytes\n\n",
+@@ -803,7 +818,7 @@ static void dump_cbmem_toc(void)
+
+ start = unpack_lb64(cbmem.start);
+
+- cbmem_area = map_memory_size(start, unpack_lb64(cbmem.size));
++ cbmem_area = map_memory_size(start, unpack_lb64(cbmem.size), 1);
+ entries = (struct cbmem_entry *)cbmem_area;
+
+ if (entries[0].magic == CBMEM_MAGIC) {
+@@ -814,12 +829,12 @@ static void dump_cbmem_toc(void)
+ rootptr -= sizeof(struct cbmem_root_pointer);
+ unmap_memory();
+ struct cbmem_root_pointer *r =
+- map_memory_size(rootptr, sizeof(*r));
++ map_memory_size(rootptr, sizeof(*r), 1);
+ if (r->magic == CBMEM_POINTER_MAGIC) {
+ struct cbmem_root *root;
+ uint64_t rootaddr = rootptr + r->root_offset;
+ unmap_memory();
+- root = map_memory_size(rootaddr, ROOT_MIN_SIZE);
++ root = map_memory_size(rootaddr, ROOT_MIN_SIZE, 1);
+ dump_dynamic_cbmem_toc(root);
+ } else
+ fprintf(stderr, "No valid coreboot CBMEM root pointer found.\n");
+@@ -1205,14 +1220,14 @@ int main(int argc, char** argv)
+ dtbuffer++;
+ }
+
+- parse_cbtable(baseaddr, cb_table_size);
++ parse_cbtable(baseaddr, cb_table_size, 1);
+ #else
+ int j;
+ static const int possible_base_addresses[] = { 0, 0xf0000 };
+
+ /* Find and parse coreboot table */
+ for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
+- if (parse_cbtable(possible_base_addresses[j], MAP_BYTES))
++ if (parse_cbtable(possible_base_addresses[j], MAP_BYTES, 1))
+ break;
+ }
+ #endif
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0002-cpu-amd-microcode-Update-microcode-parser-to-handle-.patch b/resources/libreboot/patch/kgpe-d16/0002-cpu-amd-microcode-Update-microcode-parser-to-handle-.patch
new file mode 100644
index 00000000..960cfa8f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0002-cpu-amd-microcode-Update-microcode-parser-to-handle-.patch
@@ -0,0 +1,41 @@
+From f65e4abec63a8ec3bfb5a784cecb7405c463c038 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 19:14:34 -0500
+Subject: [PATCH 002/146] cpu/amd/microcode: Update microcode parser to handle
+ expanded blob files
+
+---
+ src/cpu/amd/microcode/microcode.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/cpu/amd/microcode/microcode.c b/src/cpu/amd/microcode/microcode.c
+index 45e4bf0..ce5b08f 100644
+--- a/src/cpu/amd/microcode/microcode.c
++++ b/src/cpu/amd/microcode/microcode.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -83,13 +84,13 @@ static void amd_update_microcode(const void *ucode, size_t ucode_len,
+ const uint8_t *c = ucode;
+ const uint8_t *ucode_end = (uint8_t*)ucode + ucode_len;
+
+- while (c <= (ucode_end - 2048)) {
++ while (c <= (ucode_end - 4096)) {
+ m = (struct microcode *)c;
+ if (m->processor_rev_id == equivalent_processor_rev_id) {
+ apply_microcode_patch(m);
+ break;
+ }
+- c += 2048;
++ c += 4096;
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0003-arch-x86-boot-smbios-Add-SPD-IDs-for-Kingston-and-Co.patch b/resources/libreboot/patch/kgpe-d16/0003-arch-x86-boot-smbios-Add-SPD-IDs-for-Kingston-and-Co.patch
new file mode 100644
index 00000000..561dfaf4
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0003-arch-x86-boot-smbios-Add-SPD-IDs-for-Kingston-and-Co.patch
@@ -0,0 +1,36 @@
+From f612a5cc0afba6e1af60652283dd47f3f34dcca7 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 18:37:58 -0500
+Subject: [PATCH 003/146] arch/x86/boot/smbios: Add SPD IDs for Kingston and
+ Corsair
+
+---
+ src/arch/x86/smbios.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c
+index a1f05da..74288f9 100644
+--- a/src/arch/x86/smbios.c
++++ b/src/arch/x86/smbios.c
+@@ -124,10 +124,18 @@ static int smbios_processor_name(char *start)
+ void smbios_fill_dimm_manufacturer_from_id(uint16_t mod_id, struct smbios_type17 *t)
+ {
+ switch (mod_id) {
++ case 0x9801:
++ t->manufacturer = smbios_add_string(t->eos,
++ "Kingston");
++ break;
+ case 0x987f:
+ t->manufacturer = smbios_add_string(t->eos,
+ "Hynix");
+ break;
++ case 0x9e02:
++ t->manufacturer = smbios_add_string(t->eos,
++ "Corsair");
++ break;
+ case 0xad80:
+ t->manufacturer = smbios_add_string(t->eos,
+ "Hynix/Hyundai");
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0004-arch-x86-smbios-Add-Crucial-DIMM-manufacturer-ID.patch b/resources/libreboot/patch/kgpe-d16/0004-arch-x86-smbios-Add-Crucial-DIMM-manufacturer-ID.patch
new file mode 100644
index 00000000..4dd4068e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0004-arch-x86-smbios-Add-Crucial-DIMM-manufacturer-ID.patch
@@ -0,0 +1,27 @@
+From 4daf71b080abe86e2fe61af3adbc57a710b7ea02 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 28 Jul 2015 09:22:59 -0500
+Subject: [PATCH 004/146] arch/x86/smbios: Add Crucial DIMM manufacturer ID
+
+---
+ src/arch/x86/smbios.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c
+index 74288f9..6d645a3 100644
+--- a/src/arch/x86/smbios.c
++++ b/src/arch/x86/smbios.c
+@@ -124,6 +124,10 @@ static int smbios_processor_name(char *start)
+ void smbios_fill_dimm_manufacturer_from_id(uint16_t mod_id, struct smbios_type17 *t)
+ {
+ switch (mod_id) {
++ case 0x2c80:
++ t->manufacturer = smbios_add_string(t->eos,
++ "Crucial");
++ break;
+ case 0x9801:
+ t->manufacturer = smbios_add_string(t->eos,
+ "Kingston");
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0005-southbridge-amd-sr5650-Remove-unnecessary-register-c.patch b/resources/libreboot/patch/kgpe-d16/0005-southbridge-amd-sr5650-Remove-unnecessary-register-c.patch
new file mode 100644
index 00000000..b7a888e6
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0005-southbridge-amd-sr5650-Remove-unnecessary-register-c.patch
@@ -0,0 +1,33 @@
+From 0d0290e3866dd24c77de9114937b692bba0e9db9 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:29:20 -0500
+Subject: [PATCH 005/146] southbridge/amd/sr5650: Remove unnecessary register
+ configuration
+
+---
+ src/southbridge/amd/sr5650/early_setup.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
+index d91f3bd..ec555f8 100644
+--- a/src/southbridge/amd/sr5650/early_setup.c
++++ b/src/southbridge/amd/sr5650/early_setup.c
+@@ -1,6 +1,7 @@
+ /*
+ * This file is part of the coreboot project.
+ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -437,7 +438,6 @@ static void sr5650_por_htiu_index_init(device_t nb_dev)
+ set_htiu_enable_bits(nb_dev, 0x1D, 0x1<<2, 0x1<<2);
+ set_htiu_enable_bits(nb_dev, 0x1D, 0x1<<4, 0x1<<4);
+
+- set_nbcfg_enable_bits(cpu_f0, 0x68, 3 << 21, 0 << 21);
+ axindxc_reg(0x10, 1 << 9, 1 << 9);
+ set_pcie_enable_bits(nb_dev, 0x10 | 5 << 16, 1 << 9, 1 << 9);
+ set_htiu_enable_bits(nb_dev, 0x06, 0x1<<26, 0x1<<26);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0006-drivers-i2c-w83795-Add-full-support-for-fan-control-.patch b/resources/libreboot/patch/kgpe-d16/0006-drivers-i2c-w83795-Add-full-support-for-fan-control-.patch
new file mode 100644
index 00000000..f2f37ecf
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0006-drivers-i2c-w83795-Add-full-support-for-fan-control-.patch
@@ -0,0 +1,661 @@
+From 12a58e8598d572ee4997f0a6670796b5e82d318b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:53:20 -0500
+Subject: [PATCH 006/146] drivers/i2c/w83795: Add full support for fan
+ control, fan monitoring, and voltage monitoring
+
+---
+ src/drivers/i2c/w83795/chip.h | 146 +++++++++++++++++++
+ src/drivers/i2c/w83795/w83795.c | 301 +++++++++++++++++++++++++++------------
+ src/drivers/i2c/w83795/w83795.h | 50 +++++--
+ 3 files changed, 392 insertions(+), 105 deletions(-)
+ create mode 100644 src/drivers/i2c/w83795/chip.h
+
+diff --git a/src/drivers/i2c/w83795/chip.h b/src/drivers/i2c/w83795/chip.h
+new file mode 100644
+index 0000000..413ea87
+--- /dev/null
++++ b/src/drivers/i2c/w83795/chip.h
+@@ -0,0 +1,146 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc.
++ */
++
++struct drivers_i2c_w83795_config {
++ uint8_t fanin_ctl1;
++ uint8_t fanin_ctl2;
++
++ uint8_t temp_ctl1;
++ uint8_t temp_ctl2;
++ uint8_t temp_dtse;
++
++ uint8_t volt_ctl1;
++ uint8_t volt_ctl2;
++
++ uint8_t temp1_fan_select;
++ uint8_t temp2_fan_select;
++ uint8_t temp3_fan_select;
++ uint8_t temp4_fan_select;
++ uint8_t temp5_fan_select;
++ uint8_t temp6_fan_select;
++
++ uint8_t temp1_source_select;
++ uint8_t temp2_source_select;
++ uint8_t temp3_source_select;
++ uint8_t temp4_source_select;
++ uint8_t temp5_source_select;
++ uint8_t temp6_source_select;
++
++ uint32_t vcore1_high_limit_mv; /* mV */
++ uint32_t vcore1_low_limit_mv; /* mV */
++ uint32_t vcore2_high_limit_mv; /* mV */
++ uint32_t vcore2_low_limit_mv; /* mV */
++ uint32_t vtt_high_limit_mv; /* mV */
++ uint32_t vtt_low_limit_mv; /* mV */
++ uint32_t vsen3_high_limit_mv; /* mV */
++ uint32_t vsen3_low_limit_mv; /* mV */
++ uint32_t vsen4_high_limit_mv; /* mV */
++ uint32_t vsen4_low_limit_mv; /* mV */
++ uint32_t vsen5_high_limit_mv; /* mV */
++ uint32_t vsen5_low_limit_mv; /* mV */
++ uint32_t vsen6_high_limit_mv; /* mV */
++ uint32_t vsen6_low_limit_mv; /* mV */
++ uint32_t vsen7_high_limit_mv; /* mV */
++ uint32_t vsen7_low_limit_mv; /* mV */
++ uint32_t vsen8_high_limit_mv; /* mV */
++ uint32_t vsen8_low_limit_mv; /* mV */
++ uint32_t vsen9_high_limit_mv; /* mV */
++ uint32_t vsen9_low_limit_mv; /* mV */
++ uint32_t vsen10_high_limit_mv; /* mV */
++ uint32_t vsen10_low_limit_mv; /* mV */
++ uint32_t vsen11_high_limit_mv; /* mV */
++ uint32_t vsen11_low_limit_mv; /* mV */
++ uint32_t vsen12_high_limit_mv; /* mV */
++ uint32_t vsen12_low_limit_mv; /* mV */
++ uint32_t vsen13_high_limit_mv; /* mV */
++ uint32_t vsen13_low_limit_mv; /* mV */
++ uint32_t vdd_high_limit_mv; /* mV */
++ uint32_t vdd_low_limit_mv; /* mV */
++ uint32_t vsb_high_limit_mv; /* mV */
++ uint32_t vsb_low_limit_mv; /* mV */
++ uint32_t vbat_high_limit_mv; /* mV */
++ uint32_t vbat_low_limit_mv; /* mV */
++
++ int8_t tr1_critical_temperature; /* °C */
++ int8_t tr1_critical_hysteresis; /* °C */
++ int8_t tr1_warning_temperature; /* °C */
++ int8_t tr1_warning_hysteresis; /* °C */
++ int8_t tr2_critical_temperature; /* °C */
++ int8_t tr2_critical_hysteresis; /* °C */
++ int8_t tr2_warning_temperature; /* °C */
++ int8_t tr2_warning_hysteresis; /* °C */
++ int8_t tr3_critical_temperature; /* °C */
++ int8_t tr3_critical_hysteresis; /* °C */
++ int8_t tr3_warning_temperature; /* °C */
++ int8_t tr3_warning_hysteresis; /* °C */
++ int8_t tr4_critical_temperature; /* °C */
++ int8_t tr4_critical_hysteresis; /* °C */
++ int8_t tr4_warning_temperature; /* °C */
++ int8_t tr4_warning_hysteresis; /* °C */
++ int8_t tr5_critical_temperature; /* °C */
++ int8_t tr5_critical_hysteresis; /* °C */
++ int8_t tr5_warning_temperature; /* °C */
++ int8_t tr5_warning_hysteresis; /* °C */
++ int8_t tr6_critical_temperature; /* °C */
++ int8_t tr6_critical_hysteresis; /* °C */
++ int8_t tr6_warning_temperature; /* °C */
++ int8_t tr6_warning_hysteresis; /* °C */
++ int8_t dts_critical_temperature; /* °C */
++ int8_t dts_critical_hysteresis; /* °C */
++ int8_t dts_warning_temperature; /* °C */
++ int8_t dts_warning_hysteresis; /* °C */
++
++ int8_t temp1_critical_temperature; /* °C */
++ int8_t temp2_critical_temperature; /* °C */
++ int8_t temp3_critical_temperature; /* °C */
++ int8_t temp4_critical_temperature; /* °C */
++ int8_t temp5_critical_temperature; /* °C */
++ int8_t temp6_critical_temperature; /* °C */
++
++ int8_t temp1_target_temperature; /* °C */
++ int8_t temp2_target_temperature; /* °C */
++ int8_t temp3_target_temperature; /* °C */
++ int8_t temp4_target_temperature; /* °C */
++ int8_t temp5_target_temperature; /* °C */
++ int8_t temp6_target_temperature; /* °C */
++
++ uint8_t fan1_nonstop; /* % of full speed (0-100) */
++ uint8_t fan2_nonstop; /* % of full speed (0-100) */
++ uint8_t fan3_nonstop; /* % of full speed (0-100) */
++ uint8_t fan4_nonstop; /* % of full speed (0-100) */
++ uint8_t fan5_nonstop; /* % of full speed (0-100) */
++ uint8_t fan6_nonstop; /* % of full speed (0-100) */
++ uint8_t fan7_nonstop; /* % of full speed (0-100) */
++ uint8_t fan8_nonstop; /* % of full speed (0-100) */
++
++ uint8_t default_speed; /* % of full speed (0-100) */
++
++ uint8_t fan1_duty; /* % of full speed (0-100) */
++ uint8_t fan2_duty; /* % of full speed (0-100) */
++ uint8_t fan3_duty; /* % of full speed (0-100) */
++ uint8_t fan4_duty; /* % of full speed (0-100) */
++ uint8_t fan5_duty; /* % of full speed (0-100) */
++ uint8_t fan6_duty; /* % of full speed (0-100) */
++ uint8_t fan7_duty; /* % of full speed (0-100) */
++ uint8_t fan8_duty; /* % of full speed (0-100) */
++
++ uint8_t smbus_aux; /* 0 == device located on first SMBUS,
++ * 1 == device located on auxiliary SMBUS
++ */
++};
+diff --git a/src/drivers/i2c/w83795/w83795.c b/src/drivers/i2c/w83795/w83795.c
+index 2bbe0be..0e40710 100644
+--- a/src/drivers/i2c/w83795/w83795.c
++++ b/src/drivers/i2c/w83795/w83795.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -21,12 +22,19 @@
+ #include <arch/cpu.h>
+ #include <console/console.h>
+ #include <device/device.h>
+-#include "southbridge/amd/cimx/sb700/smbus.h" /*SMBUS_IO_BASE*/
++#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700)
++# include "southbridge/amd/sb700/smbus.h" /*SMBUS_IO_BASE*/
++#else
++# include "southbridge/amd/cimx/sb700/smbus.h" /*SMBUS_IO_BASE*/
++#endif
+ #include "w83795.h"
++#include "chip.h"
++
++static uint32_t smbus_io_base;
+
+ static int w83795_set_bank(u8 bank)
+ {
+- return do_smbus_write_byte(SMBUS_IO_BASE, W83795_DEV, W83795_REG_BANKSEL, bank);
++ return do_smbus_write_byte(smbus_io_base, W83795_DEV, W83795_REG_BANKSEL, bank);
+ }
+
+ static u8 w83795_read(u16 reg)
+@@ -35,11 +43,11 @@ static u8 w83795_read(u16 reg)
+
+ ret = w83795_set_bank(reg >> 8);
+ if (ret < 0) {
+- printk(BIOS_DEBUG, "read faild to set bank %x\n", reg >> 8);
++ printk(BIOS_DEBUG, "read failed to set bank %x\n", reg >> 8);
+ return -1;
+ }
+
+- ret = do_smbus_read_byte(SMBUS_IO_BASE, W83795_DEV, reg & 0xff);
++ ret = do_smbus_read_byte(smbus_io_base, W83795_DEV, reg & 0xff);
+ return ret;
+ }
+
+@@ -49,18 +57,18 @@ static u8 w83795_write(u16 reg, u8 value)
+
+ err = w83795_set_bank(reg >> 8);
+ if (err < 0) {
+- printk(BIOS_DEBUG, "write faild to set bank %x\n", reg >> 8);
++ printk(BIOS_DEBUG, "write failed to set bank %x\n", reg >> 8);
+ return -1;
+ }
+
+- err = do_smbus_write_byte(SMBUS_IO_BASE, W83795_DEV, reg & 0xff, value);
++ err = do_smbus_write_byte(smbus_io_base, W83795_DEV, reg & 0xff, value);
+ return err;
+ }
+
+ /*
+- * Enable Digital Temperature Sensor
++ * Configure Digital Temperature Sensor
+ */
+-static void w83795_dts_enable(u8 dts_src)
++static void w83795_dts_configure(u8 dts_src)
+ {
+ u8 val;
+
+@@ -68,45 +76,6 @@ static void w83795_dts_enable(u8 dts_src)
+ val = w83795_read(W83795_REG_DTSC);
+ val |= (dts_src & 0x01);
+ w83795_write(W83795_REG_DTSC, val);
+-
+- /* DTSE */
+- val = w83795_read(W83795_REG_DTSE);
+- val |= 0xFF;
+- w83795_write(W83795_REG_DTSE, val);
+-
+- /* store bank3 regs first before enable DTS */
+-
+- /*
+- * TD/TR1-4 thermal diode by default
+- * 0x00 Disable
+- * 0x01 thermistors on motherboard
+- * 0x10 different mode voltage
+- * 0x11 CPU internal thermal diode output
+- *
+- * TR5-6 thermistors by default TRn
+- */
+- val = 0x55; /* thermal diode */
+- w83795_write(W83795_REG_TEMP_CTRL2, val);
+-
+- /* Enable Digital Temperature Sensor */
+- val = w83795_read(W83795_REG_TEMP_CTRL1);
+- val |= W83795_REG_TEMP_CTRL1_EN_DTS; /* EN_DTS */
+- w83795_write(W83795_REG_TEMP_CTRL1, val);
+-}
+-
+-static void w83795_set_tfmr(w83795_fan_mode_t mode)
+-{
+- u8 val;
+- u8 i;
+-
+- if ((mode == SMART_FAN_MODE) || (mode == THERMAL_CRUISE_MODE)) {
+- val = 0xFF;
+- } else {
+- val = 0x00;
+- }
+-
+- for (i = 0; i < 6; i++)
+- w83795_write(W83795_REG_TFMR(i), val);
+ }
+
+ static u32 w83795_set_fan_mode(w83795_fan_mode_t mode)
+@@ -131,40 +100,12 @@ static u32 w83795_set_fan_mode(w83795_fan_mode_t mode)
+ return 0;
+ }
+
+-static void w83795_set_tss(void)
+-{
+- u8 val;
+-
+- val = 0x00;
+- w83795_write(W83795_REG_TSS(0), val); /* Temp1, 2 */
+- w83795_write(W83795_REG_TSS(1), val); /* Temp3, 4 */
+- w83795_write(W83795_REG_TSS(2), val); /* Temp5, 6 */
+-}
+-
+ static void w83795_set_fan(w83795_fan_mode_t mode)
+ {
+- u8 i;
+-
+- /* select temperature sensor (TSS)*/
+- w83795_set_tss();
+-
+- /* select Temperature to Fan mapping Relationships (TFMR)*/
+- w83795_set_tfmr(mode);
+-
+ /* set fan output controlled mode (FCMS)*/
+ w83795_set_fan_mode(mode);
+
+- /* Set Critical Temperature to Full Speed all fan (CTFS) */
+- for (i = 0; i < 6; i++) {
+- w83795_write(W83795_REG_CTFS(i), 0x50); /* default 80 celsius degree */
+- }
+-
+- if (mode == THERMAL_CRUISE_MODE) {
+- /* Set Target Temperature of Temperature Inputs (TTTI) */
+- for (i = 0; i < 6; i++) {
+- w83795_write(W83795_REG_TTTI(i), 0x28); /* default 40 celsius degree */
+- }
+- } else if (mode == SMART_FAN_MODE) {
++ if (mode == SMART_FAN_MODE) {
+ /* Set the Relative Register-at SMART FAN IV Control Mode Table */
+ //SFIV TODO
+ }
+@@ -173,12 +114,45 @@ static void w83795_set_fan(w83795_fan_mode_t mode)
+ //TODO
+ }
+
+-static void w83795_init(w83795_fan_mode_t mode, u8 dts_src)
++static uint8_t fan_pct_to_cfg_val(uint8_t percent)
+ {
+- u8 i;
+- u8 val;
++ uint16_t cfg = (((unsigned int)percent * 10000) / 3922);
++ if (cfg > 0xff)
++ cfg = 0xff;
++ return cfg;
++}
++
++static uint8_t millivolts_to_limit_value_type1(int millivolts)
++{
++ /* Datasheet v1.41 page 44 (VSEN1 - VSEN13, VTT) */
++ return ((millivolts / 2) >> 2);
++}
++
++static uint8_t millivolts_to_limit_value_type2(int millivolts)
++{
++ /* Datasheet v1.41 page 44 (3VSB, 3VDD, VBAT) */
++ return ((millivolts / 6) >> 2);
++}
++
++static uint16_t millivolts_to_limit_value_type3(int millivolts)
++{
++ /* Datasheet v1.41 page 44 (VDSEN14 - VDSEN17) */
++ return (millivolts / 2);
++}
++
++static void w83795_init(struct device *dev, w83795_fan_mode_t mode, u8 dts_src)
++{
++ struct drivers_i2c_w83795_config *config = dev->chip_info;
++ uint8_t i;
++ uint8_t val;
++ uint16_t limit_value;
++
++ if (config->smbus_aux)
++ smbus_io_base = SMBUS_AUX_IO_BASE;
++ else
++ smbus_io_base = SMBUS_IO_BASE;
+
+- if (do_smbus_read_byte(SMBUS_IO_BASE, W83795_DEV, 0x00) < 0) {
++ if (do_smbus_read_byte(smbus_io_base, W83795_DEV, 0x00) < 0) {
+ printk(BIOS_ERR, "W83795G/ADG Nuvoton H/W Monitor not found\n");
+ return;
+ }
+@@ -192,18 +166,156 @@ static void w83795_init(w83795_fan_mode_t mode, u8 dts_src)
+ val |= W83795_REG_CONFIG_INIT;
+ w83795_write(W83795_REG_CONFIG, val);
+
+- /* Fan monitoring setting */
+- val = 0xFF; /* FAN1-FAN8 */
+- w83795_write(W83795_REG_FANIN_CTRL1, val);
+- val = 0x3F; /* FAN9-FAN14 */
+- w83795_write(W83795_REG_FANIN_CTRL2, val);
++ /* Fan monitor settings */
++ w83795_write(W83795_REG_FANIN_CTRL1, config->fanin_ctl1);
++ w83795_write(W83795_REG_FANIN_CTRL2, config->fanin_ctl2);
++
++ /* Temperature thresholds */
++ w83795_write(W83795_REG_TEMP_CRIT(0), config->tr1_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(0), config->tr1_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(0), config->tr1_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(0), config->tr1_warning_hysteresis);
++ w83795_write(W83795_REG_TEMP_CRIT(1), config->tr2_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(1), config->tr2_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(1), config->tr2_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(1), config->tr2_warning_hysteresis);
++ w83795_write(W83795_REG_TEMP_CRIT(2), config->tr3_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(2), config->tr3_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(2), config->tr3_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(2), config->tr3_warning_hysteresis);
++ w83795_write(W83795_REG_TEMP_CRIT(3), config->tr4_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(3), config->tr4_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(3), config->tr4_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(3), config->tr4_warning_hysteresis);
++ w83795_write(W83795_REG_TEMP_CRIT(4), config->tr5_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(4), config->tr5_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(4), config->tr5_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(4), config->tr5_warning_hysteresis);
++ w83795_write(W83795_REG_TEMP_CRIT(5), config->tr6_critical_temperature);
++ w83795_write(W83795_REG_TEMP_CRIT_HYSTER(5), config->tr6_critical_hysteresis);
++ w83795_write(W83795_REG_TEMP_WARN(5), config->tr6_warning_temperature);
++ w83795_write(W83795_REG_TEMP_WARN_HYSTER(5), config->tr6_warning_hysteresis);
++
++ /* DTS enable */
++ w83795_write(W83795_REG_DTSE, config->temp_dtse);
++
++ /* DTS temperature thresholds */
++ w83795_write(W83795_REG_DTS_CRIT, config->dts_critical_temperature);
++ w83795_write(W83795_REG_DTS_CRIT_HYSTER, config->dts_critical_hysteresis);
++ w83795_write(W83795_REG_DTS_WARN, config->dts_warning_temperature);
++ w83795_write(W83795_REG_DTS_WARN_HYSTER, config->dts_warning_hysteresis);
++
++ /* Configure DTS registers in bank3 before enabling DTS */
++ w83795_dts_configure(dts_src);
++
++ /* Temperature monitor settings */
++ w83795_write(W83795_REG_TEMP_CTRL1, config->temp_ctl1);
++ w83795_write(W83795_REG_TEMP_CTRL2, config->temp_ctl2);
++
++ /* Temperature to fan mappings */
++ w83795_write(W83795_REG_TFMR(0), config->temp1_fan_select);
++ w83795_write(W83795_REG_TFMR(1), config->temp2_fan_select);
++ w83795_write(W83795_REG_TFMR(2), config->temp3_fan_select);
++ w83795_write(W83795_REG_TFMR(3), config->temp4_fan_select);
++ w83795_write(W83795_REG_TFMR(4), config->temp5_fan_select);
++ w83795_write(W83795_REG_TFMR(5), config->temp6_fan_select);
++
++ /* Temperature data source to temperature mappings */
++ w83795_write(W83795_REG_T12TSS, ((config->temp2_source_select & 0xff) << 8) | (config->temp1_source_select & 0xff));
++ w83795_write(W83795_REG_T34TSS, ((config->temp4_source_select & 0xff) << 8) | (config->temp3_source_select & 0xff));
++ w83795_write(W83795_REG_T56TSS, ((config->temp6_source_select & 0xff) << 8) | (config->temp5_source_select & 0xff));
+
+- /* enable monitoring operations */
+- val = w83795_read(W83795_REG_CONFIG);
+- val |= W83795_REG_CONFIG_START;
+- w83795_write(W83795_REG_CONFIG, val);
++ /* Set Critical Temperature to Full Speed all fan (CTFS) */
++ w83795_write(W83795_REG_CTFS(0), config->temp1_critical_temperature);
++ w83795_write(W83795_REG_CTFS(1), config->temp2_critical_temperature);
++ w83795_write(W83795_REG_CTFS(2), config->temp3_critical_temperature);
++ w83795_write(W83795_REG_CTFS(3), config->temp4_critical_temperature);
++ w83795_write(W83795_REG_CTFS(4), config->temp5_critical_temperature);
++ w83795_write(W83795_REG_CTFS(5), config->temp6_critical_temperature);
++
++ /* Set fan control target temperatures */
++ w83795_write(W83795_REG_TTTI(0), config->temp1_target_temperature);
++ w83795_write(W83795_REG_TTTI(1), config->temp2_target_temperature);
++ w83795_write(W83795_REG_TTTI(2), config->temp3_target_temperature);
++ w83795_write(W83795_REG_TTTI(3), config->temp4_target_temperature);
++ w83795_write(W83795_REG_TTTI(4), config->temp5_target_temperature);
++ w83795_write(W83795_REG_TTTI(5), config->temp6_target_temperature);
++
++ /* Set fan stall prevention parameters */
++ w83795_write(W83795_REG_FAN_NONSTOP(0), config->fan1_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(1), config->fan2_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(2), config->fan3_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(3), config->fan4_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(4), config->fan5_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(5), config->fan6_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(6), config->fan7_nonstop);
++ w83795_write(W83795_REG_FAN_NONSTOP(7), config->fan8_nonstop);
++
++ /* Set fan default speed */
++ w83795_write(W83795_REG_DFSP, fan_pct_to_cfg_val(config->default_speed));
++
++ /* Set initial fan speeds */
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(0), fan_pct_to_cfg_val(config->fan1_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(1), fan_pct_to_cfg_val(config->fan2_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(2), fan_pct_to_cfg_val(config->fan3_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(3), fan_pct_to_cfg_val(config->fan4_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(4), fan_pct_to_cfg_val(config->fan5_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(5), fan_pct_to_cfg_val(config->fan6_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(6), fan_pct_to_cfg_val(config->fan7_duty));
++ w83795_write(W83795_REG_FAN_MANUAL_SPEED(7), fan_pct_to_cfg_val(config->fan8_duty));
++
++ /* Voltage monitor settings */
++ w83795_write(W83795_REG_VOLT_CTRL1, config->volt_ctl1);
++ w83795_write(W83795_REG_VOLT_CTRL2, config->volt_ctl2);
++
++ /* Voltage high/low limits */
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(0), millivolts_to_limit_value_type1(config->vcore1_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(0), millivolts_to_limit_value_type1(config->vcore1_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(1), millivolts_to_limit_value_type1(config->vcore2_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(1), millivolts_to_limit_value_type1(config->vcore2_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(2), millivolts_to_limit_value_type1(config->vsen3_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(2), millivolts_to_limit_value_type1(config->vsen3_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(3), millivolts_to_limit_value_type1(config->vsen4_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(3), millivolts_to_limit_value_type1(config->vsen4_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(4), millivolts_to_limit_value_type1(config->vsen5_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(4), millivolts_to_limit_value_type1(config->vsen5_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(5), millivolts_to_limit_value_type1(config->vsen6_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(5), millivolts_to_limit_value_type1(config->vsen6_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(6), millivolts_to_limit_value_type1(config->vsen7_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(6), millivolts_to_limit_value_type1(config->vsen7_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(7), millivolts_to_limit_value_type1(config->vsen8_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(7), millivolts_to_limit_value_type1(config->vsen8_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(8), millivolts_to_limit_value_type1(config->vsen9_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(8), millivolts_to_limit_value_type1(config->vsen9_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(9), millivolts_to_limit_value_type1(config->vsen10_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(9), millivolts_to_limit_value_type1(config->vsen10_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(10), millivolts_to_limit_value_type1(config->vsen11_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(10), millivolts_to_limit_value_type1(config->vsen11_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(11), millivolts_to_limit_value_type1(config->vtt_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(11), millivolts_to_limit_value_type1(config->vtt_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(12), millivolts_to_limit_value_type2(config->vdd_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(12), millivolts_to_limit_value_type2(config->vdd_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(13), millivolts_to_limit_value_type2(config->vsb_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(13), millivolts_to_limit_value_type2(config->vsb_low_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_HIGH(14), millivolts_to_limit_value_type2(config->vbat_high_limit_mv));
++ w83795_write(W83795_REG_VOLT_LIM_LOW(14), millivolts_to_limit_value_type2(config->vbat_low_limit_mv));
++
++ /* VSEN12 limits */
++ limit_value = millivolts_to_limit_value_type3(config->vsen12_high_limit_mv);
++ w83795_write(W83795_REG_VOLT_LIM_HIGH_2_M(4), limit_value >> 2);
++ w83795_write(W83795_REG_VOLT_LIM_HIGH_2_L(4), limit_value & 0x3);
++ limit_value = millivolts_to_limit_value_type3(config->vsen12_low_limit_mv);
++ w83795_write(W83795_REG_VOLT_LIM_LOW_2_M(4), limit_value >> 2);
++ w83795_write(W83795_REG_VOLT_LIM_LOW_2_L(4), limit_value & 0x3);
++
++ /* VSEN13 limits */
++ limit_value = millivolts_to_limit_value_type3(config->vsen13_high_limit_mv);
++ w83795_write(W83795_REG_VOLT_LIM_HIGH_2_M(5), limit_value >> 2);
++ w83795_write(W83795_REG_VOLT_LIM_HIGH_2_L(5), limit_value & 0x3);
++ limit_value = millivolts_to_limit_value_type3(config->vsen13_low_limit_mv);
++ w83795_write(W83795_REG_VOLT_LIM_LOW_2_M(5), limit_value >> 2);
++ w83795_write(W83795_REG_VOLT_LIM_LOW_2_L(5), limit_value & 0x3);
+
+- w83795_dts_enable(dts_src);
+ w83795_set_fan(mode);
+
+ printk(BIOS_INFO, "Fan CTFS(celsius) TTTI(celsius)\n");
+@@ -219,6 +331,11 @@ static void w83795_init(w83795_fan_mode_t mode, u8 dts_src)
+ val = w83795_read(W83795_REG_DTS(i));
+ printk(BIOS_DEBUG, "DTS%x ReadOut=%x\n", i, val);
+ }
++
++ /* start monitoring operation */
++ val = w83795_read(W83795_REG_CONFIG);
++ val |= W83795_REG_CONFIG_START;
++ w83795_write(W83795_REG_CONFIG, val);
+ }
+
+ static void w83795_hwm_init(struct device *dev)
+@@ -232,9 +349,9 @@ static void w83795_hwm_init(struct device *dev)
+ die("CPU: missing cpu device structure");
+
+ if (cpu->vendor == X86_VENDOR_AMD)
+- w83795_init(THERMAL_CRUISE_MODE, DTS_SRC_AMD_SBTSI);
++ w83795_init(dev, THERMAL_CRUISE_MODE, DTS_SRC_AMD_SBTSI);
+ else if (cpu->vendor == X86_VENDOR_INTEL)
+- w83795_init(THERMAL_CRUISE_MODE, DTS_SRC_INTEL_PECI);
++ w83795_init(dev, THERMAL_CRUISE_MODE, DTS_SRC_INTEL_PECI);
+ else
+ printk(BIOS_ERR, "Neither AMD nor INTEL CPU detected\n");
+ }
+diff --git a/src/drivers/i2c/w83795/w83795.h b/src/drivers/i2c/w83795/w83795.h
+index cac4d5f..59fa273 100644
+--- a/src/drivers/i2c/w83795/w83795.h
++++ b/src/drivers/i2c/w83795/w83795.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -29,6 +30,8 @@
+ #define W83795_REG_CONFIG_CONFIG48 0x04
+ #define W83795_REG_CONFIG_INIT 0x80
+
++#define W83795_REG_VOLT_CTRL1 0x02
++#define W83795_REG_VOLT_CTRL2 0x03
+ #define W83795_REG_TEMP_CTRL1 0x04 /* Temperature Monitoring Control Register */
+ #define W83795_REG_TEMP_CTRL2 0x05 /* Temperature Monitoring Control Register */
+ #define W83795_REG_FANIN_CTRL1 0x06
+@@ -37,37 +40,58 @@
+ #define DTS_SRC_INTEL_PECI (0 << 0)
+ #define DTS_SRC_AMD_SBTSI (1 << 0)
+
+-#define W83795_REG_TSS(n) (0x209 + (n)) /* Temperature Source Selection Register */
+ #define W83795_REG_TTTI(n) (0x260 + (n)) /* Target temperature W83795G/ADG will try to tune the fan output to keep */
+ #define W83795_REG_CTFS(n) (0x268 + (n)) /* Critical Temperature to Full Speed all fan */
+-#define W83795_REG_HT(n) (0x270 + (n)) /* Hysteresis of Temperature */
+ #define W83795_REG_DTSC 0x301 /* Digital Temperature Sensor Configuration */
+
+ #define W83795_REG_DTSE 0x302 /* Digital Temperature Sensor Enable */
+ #define W83795_REG_DTS(n) (0x26 + (n))
+ #define W83795_REG_VRLSB 0x3C
+
+-#define W83795_TEMP_REG_TR1 0x21
+-#define W83795_TEMP_REG_TR2 0x22
+-#define W83795_TEMP_REG_TR3 0x23
+-#define W83795_TEMP_REG_TR4 0x24
+-#define W83795_TEMP_REG_TR5 0x1F
+-#define W83795_TEMP_REG_TR6 0x20
++#define W83795_REG_TEMP_TR1 0x21
++#define W83795_REG_TEMP_TR2 0x22
++#define W83795_REG_TEMP_TR3 0x23
++#define W83795_REG_TEMP_TR4 0x24
++#define W83795_REG_TEMP_TR5 0x1F
++#define W83795_REG_TEMP_TR6 0x20
++
++#define W83795_REG_VOLT_LIM_HIGH(n) (0x70 + (n * 2)) /* Voltage high limit (0 == VSEN1) */
++#define W83795_REG_VOLT_LIM_LOW(n) (0x71 + (n * 2)) /* Voltage low limit (0 == VSEN1) */
++#define W83795_REG_VOLT_LIM_HIGH_2_M(n) (0x96 + (n * 4)) /* Voltage high limit MSB (0 == VDSEN14) */
++#define W83795_REG_VOLT_LIM_LOW_2_M(n) (0x97 + (n * 4)) /* Voltage low limit MSB (0 == VDSEN14) */
++#define W83795_REG_VOLT_LIM_HIGH_2_L(n) (0x98 + (n * 4)) /* Voltage high limit LSB (0 == VDSEN14) */
++#define W83795_REG_VOLT_LIM_LOW_2_L(n) (0x99 + (n * 4)) /* Voltage low limit LSB (0 == VDSEN14) */
++
++#define W83795_REG_TEMP_CRIT(n) (0x96 + (n * 4)) /* Temperature critical limit */
++#define W83795_REG_TEMP_CRIT_HYSTER(n) (0x97 + (n * 4)) /* Temperature critical limit hysteresis */
++#define W83795_REG_TEMP_WARN(n) (0x98 + (n * 4)) /* Temperature warning limit */
++#define W83795_REG_TEMP_WARN_HYSTER(n) (0x99 + (n * 4)) /* Temperature warning limit hysteresis */
++
++#define W83795_REG_DTS_CRIT 0xB2 /* Temperature critical limit */
++#define W83795_REG_DTS_CRIT_HYSTER 0xB3 /* Temperature critical limit hysteresis */
++#define W83795_REG_DTS_WARN 0xB4 /* Temperature warning limit */
++#define W83795_REG_DTS_WARN_HYSTER 0xB5 /* Temperature warning limit hysteresis */
+
+ #define W83795_REG_FCMS1 0x201
+ #define W83795_REG_FCMS2 0x208
+-#define W83795_REG_TFMR(n) (0x202 + (n)) /*temperature to fam mappig*/
++#define W83795_REG_TFMR(n) (0x202 + (n)) /* Temperature to fan mapping */
++#define W83795_REG_T12TSS 0x209 /* Temperature Source Selection Register 1 */
++#define W83795_REG_T34TSS 0x20A /* Temperature Source Selection Register 2 */
++#define W83795_REG_T56TSS 0x20B /* Temperature Source Selection Register 3 */
++#define W83795_REG_FAN_MANUAL_SPEED(n) (0x210 + n)
+ #define W83795_REG_DFSP 0x20C
+
++#define W83795_REG_FAN_NONSTOP(n) (0x228 + (n)) /* Fan Nonstop Value */
++
+ #define W83795_REG_FTSH(n) (0x240 + (n) * 2)
+ #define W83795_REG_FTSL(n) (0x241 + (n) * 2)
+ #define W83795_REG_TFTS 0x250
+
+ typedef enum w83795_fan_mode {
+- SPEED_CRUISE_MODE, ///< Fan Speed Cruise mode keeps the fan speed in a specified range
+- THERMAL_CRUISE_MODE, ///< Thermal Cruise mode is an algorithm to control the fan speed to keep the temperature source around the TTTI
+- SMART_FAN_MODE, ///< Smart Fan mode offers 6 slopes to control the fan speed
+- MANUAL_MODE, ///< control manually
++ SPEED_CRUISE_MODE = 0, ///< Fan Speed Cruise mode keeps the fan speed in a specified range
++ THERMAL_CRUISE_MODE = 1, ///< Thermal Cruise mode is an algorithm to control the fan speed to keep the temperature source around the TTTI
++ SMART_FAN_MODE = 2, ///< Smart Fan mode offers 6 slopes to control the fan speed
++ MANUAL_MODE = 3, ///< control manually
+ } w83795_fan_mode_t;
+
+ #endif
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0007-drivers-aspeed-Add-native-text-mode-VGA-support-for-.patch b/resources/libreboot/patch/kgpe-d16/0007-drivers-aspeed-Add-native-text-mode-VGA-support-for-.patch
new file mode 100644
index 00000000..4f1751df
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0007-drivers-aspeed-Add-native-text-mode-VGA-support-for-.patch
@@ -0,0 +1,3673 @@
+From 27f2cdc381f7bac82f868acef86bcc95467fe24c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:38:09 -0500
+Subject: [PATCH 007/146] drivers/aspeed: Add native text mode VGA support for
+ the AST2050
+
+---
+ src/drivers/aspeed/Kconfig | 2 +
+ src/drivers/aspeed/Makefile.inc | 1 +
+ src/drivers/aspeed/ast2050/Kconfig | 14 +
+ src/drivers/aspeed/ast2050/Makefile.inc | 1 +
+ src/drivers/aspeed/ast2050/ast2050.c | 83 ++
+ src/drivers/aspeed/common/Kconfig | 10 +
+ src/drivers/aspeed/common/Makefile.inc | 1 +
+ src/drivers/aspeed/common/aspeed_coreboot.h | 210 ++++
+ src/drivers/aspeed/common/ast_dp501.c | 443 +++++++
+ src/drivers/aspeed/common/ast_dram_tables.h | 165 +++
+ src/drivers/aspeed/common/ast_drv.h | 223 ++++
+ src/drivers/aspeed/common/ast_main.c | 393 +++++++
+ src/drivers/aspeed/common/ast_post.c | 1679 +++++++++++++++++++++++++++
+ src/drivers/aspeed/common/ast_tables.h | 305 +++++
+ src/include/device/pci_ids.h | 3 +
+ 15 files changed, 3533 insertions(+)
+ create mode 100644 src/drivers/aspeed/Kconfig
+ create mode 100644 src/drivers/aspeed/Makefile.inc
+ create mode 100644 src/drivers/aspeed/ast2050/Kconfig
+ create mode 100644 src/drivers/aspeed/ast2050/Makefile.inc
+ create mode 100644 src/drivers/aspeed/ast2050/ast2050.c
+ create mode 100644 src/drivers/aspeed/common/Kconfig
+ create mode 100644 src/drivers/aspeed/common/Makefile.inc
+ create mode 100644 src/drivers/aspeed/common/aspeed_coreboot.h
+ create mode 100644 src/drivers/aspeed/common/ast_dp501.c
+ create mode 100644 src/drivers/aspeed/common/ast_dram_tables.h
+ create mode 100644 src/drivers/aspeed/common/ast_drv.h
+ create mode 100644 src/drivers/aspeed/common/ast_main.c
+ create mode 100644 src/drivers/aspeed/common/ast_post.c
+ create mode 100644 src/drivers/aspeed/common/ast_tables.h
+
+diff --git a/src/drivers/aspeed/Kconfig b/src/drivers/aspeed/Kconfig
+new file mode 100644
+index 0000000..27469b5
+--- /dev/null
++++ b/src/drivers/aspeed/Kconfig
+@@ -0,0 +1,2 @@
++source src/drivers/aspeed/common/Kconfig
++source src/drivers/aspeed/ast2050/Kconfig
+\ No newline at end of file
+diff --git a/src/drivers/aspeed/Makefile.inc b/src/drivers/aspeed/Makefile.inc
+new file mode 100644
+index 0000000..955a213
+--- /dev/null
++++ b/src/drivers/aspeed/Makefile.inc
+@@ -0,0 +1 @@
++subdirs-y += common ast2050
+\ No newline at end of file
+diff --git a/src/drivers/aspeed/ast2050/Kconfig b/src/drivers/aspeed/ast2050/Kconfig
+new file mode 100644
+index 0000000..f110d58
+--- /dev/null
++++ b/src/drivers/aspeed/ast2050/Kconfig
+@@ -0,0 +1,14 @@
++config DRIVERS_ASPEED_AST2050
++ bool
++
++if DRIVERS_ASPEED_AST2050
++
++config DEVICE_SPECIFIC_OPTIONS # dummy
++ def_bool y
++ select DRIVERS_ASPEED_AST_COMMON
++
++config NATIVE_VGA_INIT_USE_EDID
++ bool
++ default n
++
++endif # DRIVERS_ASPEED_AST2050
+diff --git a/src/drivers/aspeed/ast2050/Makefile.inc b/src/drivers/aspeed/ast2050/Makefile.inc
+new file mode 100644
+index 0000000..3ba9dde
+--- /dev/null
++++ b/src/drivers/aspeed/ast2050/Makefile.inc
+@@ -0,0 +1 @@
++ramstage-$(CONFIG_DRIVERS_ASPEED_AST2050) += ast2050.c
+\ No newline at end of file
+diff --git a/src/drivers/aspeed/ast2050/ast2050.c b/src/drivers/aspeed/ast2050/ast2050.c
+new file mode 100644
+index 0000000..cc090bb
+--- /dev/null
++++ b/src/drivers/aspeed/ast2050/ast2050.c
+@@ -0,0 +1,83 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++#include <delay.h>
++#include <stdlib.h>
++#include <string.h>
++#include <arch/io.h>
++#include <edid.h>
++
++#include <console/console.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <device/pci_ops.h>
++
++#include <pc80/vga.h>
++
++#include "../common/aspeed_coreboot.h"
++#include "../common/ast_drv.h"
++
++static void aspeed_ast2050_set_resources(device_t dev)
++{
++ /* Reserve VGA regions */
++ mmio_resource(dev, 3, 0xa0000 >> 10, 0x1ffff >> 10);
++
++ /* Run standard resource set routine */
++ pci_dev_set_resources(dev);
++}
++
++static void aspeed_ast2050_init(struct device *dev)
++{
++ u8 ret;
++ struct drm_device drm_dev;
++
++ drm_dev.pdev = dev;
++
++ printk(BIOS_INFO, "ASpeed AST2050: initializing video device\n");
++ ret = ast_driver_load(&drm_dev, 0);
++
++ /* Unlock extended configuration registers */
++ outb(0x80, 0x3d4); outb(0xa8, 0x3d5);
++
++ /* Set CRT Request Threshold */
++ outb(0xa6, 0x3d4); outb(0x2f, 0x3d5);
++ outb(0xa7, 0x3d4); outb(0x3f, 0x3d5);
++
++ /* Initialize standard VGA text mode */
++ vga_io_init();
++ vga_textmode_init();
++ printk(BIOS_INFO, "ASpeed VGA text mode initialized\n");
++
++ /* if we don't have console, at least print something... */
++ vga_line_write(0, "ASpeed VGA text mode initialized");
++}
++
++static struct device_operations aspeed_ast2050_ops = {
++ .read_resources = pci_dev_read_resources,
++ .set_resources = aspeed_ast2050_set_resources,
++ .enable_resources = pci_dev_enable_resources,
++ .init = aspeed_ast2050_init,
++ .scan_bus = 0,
++};
++
++static const struct pci_driver aspeed_ast2050_driver __pci_driver = {
++ .ops = &aspeed_ast2050_ops,
++ .vendor = PCI_VENDOR_ID_ASPEED,
++ .device = PCI_DEVICE_ID_ASPEED_AST2050_VGA,
++};
+diff --git a/src/drivers/aspeed/common/Kconfig b/src/drivers/aspeed/common/Kconfig
+new file mode 100644
+index 0000000..0f7056b
+--- /dev/null
++++ b/src/drivers/aspeed/common/Kconfig
+@@ -0,0 +1,10 @@
++config DRIVERS_ASPEED_AST_COMMON
++ bool
++
++if !MAINBOARD_DO_NATIVE_VGA_INIT
++
++config DEVICE_SPECIFIC_OPTIONS # dummy
++ def_bool y
++ select VGA
++
++endif # MAINBOARD_DO_NATIVE_VGA_INIT
+diff --git a/src/drivers/aspeed/common/Makefile.inc b/src/drivers/aspeed/common/Makefile.inc
+new file mode 100644
+index 0000000..75f8b48
+--- /dev/null
++++ b/src/drivers/aspeed/common/Makefile.inc
+@@ -0,0 +1 @@
++ramstage-$(CONFIG_DRIVERS_ASPEED_AST_COMMON) += ast_dp501.c ast_main.c ast_post.c
+diff --git a/src/drivers/aspeed/common/aspeed_coreboot.h b/src/drivers/aspeed/common/aspeed_coreboot.h
+new file mode 100644
+index 0000000..237c23f
+--- /dev/null
++++ b/src/drivers/aspeed/common/aspeed_coreboot.h
+@@ -0,0 +1,210 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#ifndef _ASPEED_COREBOOT_
++#define _ASPEED_COREBOOT_
++
++#include <delay.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <string.h>
++#include <arch/io.h>
++
++#include <console/console.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <device/pci_ops.h>
++
++/* coreboot <--> kernel code interface */
++#define __iomem
++typedef u64 phys_addr_t;
++#define pci_dev device
++
++#define SZ_16M 0x01000000
++
++#define min_t(type, x, y) ({ \
++ type __min1 = (x); \
++ type __min2 = (y); \
++ __min1 < __min2 ? __min1 : __min2; })
++
++#define dev_info(dev, format, arg...) printk(BIOS_INFO, "ASpeed VGA: " format, ##arg)
++#define dev_dbg(dev, format, arg...) printk(BIOS_DEBUG, "ASpeed VGA: " format, ##arg)
++#define dev_err(dev, format, arg...) printk(BIOS_ERR, "ASpeed VGA: " format, ##arg)
++
++#define pr_info(format, arg...) printk(BIOS_INFO, "ASpeed VGA: " format, ##arg)
++#define pr_debug(format, arg...) printk(BIOS_INFO, "ASpeed VGA: " format, ##arg)
++#define pr_err(format, arg...) printk(BIOS_ERR, "ASpeed VGA: " format, ##arg)
++
++#define DRM_INFO pr_info
++
++#define GFP_KERNEL 0
++#define GFP_ATOMIC 1
++#define kfree(address) free(address)
++
++#define EIO 5
++#define ENOMEM 12
++
++struct firmware {
++ size_t size;
++ const u8 *data;
++ struct page **pages;
++
++ /* firmware loader private fields */
++ void *priv;
++};
++
++struct drm_device {
++ struct pci_dev *pdev;
++ void *dev_private;
++};
++
++static inline void *kzalloc(size_t size, int flags) {
++ void* ptr = malloc(size);
++ memset(ptr, 0, size);
++ return ptr;
++}
++
++static inline void writel(u32 val, volatile void *addr) {
++ *(u32*)addr = val;
++}
++
++static inline u32 readl(const volatile void *addr) {
++ return *(u32*)addr;
++}
++
++static inline void writew(u16 val, volatile void *addr) {
++ *(u16*)addr = val;
++}
++
++static inline u16 readw(const volatile void *addr) {
++ return *(u16*)addr;
++}
++
++static inline void writeb(u8 val, volatile void *addr) {
++ *(u8*)addr = val;
++}
++
++static inline u8 readb(const volatile void *addr) {
++ return *(u8*)addr;
++}
++
++static inline int pci_read_config_dword(struct pci_dev *dev, int where,
++ u32 *val)
++{
++ *val = pci_read_config32(dev, where);
++ return 0;
++}
++
++static inline int pci_write_config_dword(struct pci_dev *dev, int where,
++ u32 val)
++{
++ pci_write_config32(dev, where, val);
++ return 0;
++}
++
++static inline int pci_read_config_byte(struct pci_dev *dev, int where,
++ u8 *val)
++{
++ *val = pci_read_config8(dev, where);
++ return 0;
++}
++
++static inline struct resource* resource_at_bar(struct pci_dev *dev, u8 bar) {
++ struct resource *res = dev->resource_list;
++ int i;
++ for (i = 0; i < bar; i++) {
++ res = res->next;
++ if (res == NULL)
++ return NULL;
++ }
++
++ return res;
++}
++
++static inline resource_t pci_resource_len(struct pci_dev *dev, u8 bar) {
++ struct resource *res = resource_at_bar(dev, bar);
++ if (res)
++ return res->size;
++ else
++ return 0;
++}
++
++static inline resource_t pci_resource_start(struct pci_dev *dev, u8 bar) {
++ struct resource *res = resource_at_bar(dev, bar);
++ if (res)
++ return res->base;
++ else
++ return 0;
++}
++
++static inline unsigned int ioread32(void __iomem *p) {
++ return readl(p);
++}
++
++static inline void iowrite32(u32 val, void __iomem *p) {
++ writel(val, p);
++}
++
++static inline unsigned int ioread16(void __iomem *p) {
++ return readw(p);
++}
++
++static inline void iowrite16(u16 val, void __iomem *p) {
++ writew(val, p);
++}
++
++static inline unsigned int ioread8(void __iomem *p) {
++ return readb(p);
++}
++
++static inline void iowrite8(u8 val, void __iomem *p) {
++ writeb(val, p);
++}
++
++static inline unsigned int ioread_cbio32(void __iomem *p) {
++ return inl((uint16_t)((intptr_t)p));
++}
++
++static inline void iowrite_cbio32(u32 val, void __iomem *p) {
++ outl(val, (uint16_t)((intptr_t)p));
++}
++
++static inline unsigned int ioread_cbio16(void __iomem *p) {
++ return inw((uint16_t)((intptr_t)p));
++}
++
++static inline void iowrite_cbio16(u16 val, void __iomem *p) {
++ outw(val, (uint16_t)((intptr_t)p));
++}
++
++static inline unsigned int ioread_cbio8(void __iomem *p) {
++ return inb((uint16_t)((intptr_t)p));
++}
++
++static inline void iowrite_cbio8(u8 val, void __iomem *p) {
++ outb(val, (uint16_t)((intptr_t)p));
++}
++
++static inline void msleep(unsigned int msecs) {
++ udelay(msecs * 1000);
++}
++
++#endif
+\ No newline at end of file
+diff --git a/src/drivers/aspeed/common/ast_dp501.c b/src/drivers/aspeed/common/ast_dp501.c
+new file mode 100644
+index 0000000..5be8ec3
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_dp501.c
+@@ -0,0 +1,443 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * File taken from the Linux ast driver (v3.18.5)
++ * Coreboot-specific includes added at top and/or contents modified
++ * as needed to function within the coreboot environment.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc.
++ */
++
++#include "ast_drv.h"
++
++static void send_ack(struct ast_private *ast)
++{
++ u8 sendack;
++ sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
++ sendack |= 0x80;
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
++}
++
++static void send_nack(struct ast_private *ast)
++{
++ u8 sendack;
++ sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
++ sendack &= ~0x80;
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
++}
++
++static bool wait_ack(struct ast_private *ast)
++{
++ u8 waitack;
++ u32 retry = 0;
++ do {
++ waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
++ waitack &= 0x80;
++ udelay(100);
++ } while ((!waitack) && (retry++ < 1000));
++
++ if (retry < 1000)
++ return true;
++ else
++ return false;
++}
++
++static bool wait_nack(struct ast_private *ast)
++{
++ u8 waitack;
++ u32 retry = 0;
++ do {
++ waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
++ waitack &= 0x80;
++ udelay(100);
++ } while ((waitack) && (retry++ < 1000));
++
++ if (retry < 1000)
++ return true;
++ else
++ return false;
++}
++
++static void set_cmd_trigger(struct ast_private *ast)
++{
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40);
++}
++
++static void clear_cmd_trigger(struct ast_private *ast)
++{
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00);
++}
++
++#if 0
++static bool wait_fw_ready(struct ast_private *ast)
++{
++ u8 waitready;
++ u32 retry = 0;
++ do {
++ waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
++ waitready &= 0x40;
++ udelay(100);
++ } while ((!waitready) && (retry++ < 1000));
++
++ if (retry < 1000)
++ return true;
++ else
++ return false;
++}
++#endif
++
++static bool ast_write_cmd(struct drm_device *dev, u8 data)
++{
++ struct ast_private *ast = dev->dev_private;
++ int retry = 0;
++ if (wait_nack(ast)) {
++ send_nack(ast);
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
++ send_ack(ast);
++ set_cmd_trigger(ast);
++ do {
++ if (wait_ack(ast)) {
++ clear_cmd_trigger(ast);
++ send_nack(ast);
++ return true;
++ }
++ } while (retry++ < 100);
++ }
++ clear_cmd_trigger(ast);
++ send_nack(ast);
++ return false;
++}
++
++static bool ast_write_data(struct drm_device *dev, u8 data)
++{
++ struct ast_private *ast = dev->dev_private;
++
++ if (wait_nack(ast)) {
++ send_nack(ast);
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
++ send_ack(ast);
++ if (wait_ack(ast)) {
++ send_nack(ast);
++ return true;
++ }
++ }
++ send_nack(ast);
++ return false;
++}
++
++#if 0
++static bool ast_read_data(struct drm_device *dev, u8 *data)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 tmp;
++
++ *data = 0;
++
++ if (wait_ack(ast) == false)
++ return false;
++ tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff);
++ *data = tmp;
++ if (wait_nack(ast) == false) {
++ send_nack(ast);
++ return false;
++ }
++ send_nack(ast);
++ return true;
++}
++
++static void clear_cmd(struct ast_private *ast)
++{
++ send_nack(ast);
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00);
++}
++#endif
++
++void ast_set_dp501_video_output(struct drm_device *dev, u8 mode)
++{
++ ast_write_cmd(dev, 0x40);
++ ast_write_data(dev, mode);
++
++ msleep(10);
++}
++
++static u32 get_fw_base(struct ast_private *ast)
++{
++ return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
++}
++
++bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
++{
++ struct ast_private *ast = dev->dev_private;
++ u32 i, data;
++ u32 boot_address;
++
++ data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
++ if (data) {
++ boot_address = get_fw_base(ast);
++ for (i = 0; i < size; i += 4)
++ *(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i);
++ return true;
++ }
++ return false;
++}
++
++bool ast_launch_m68k(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u32 i, data, len = 0;
++ u32 boot_address;
++ u8 *fw_addr = NULL;
++ u8 jreg;
++
++ data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
++ if (!data) {
++
++ if (ast->dp501_fw_addr) {
++ fw_addr = ast->dp501_fw_addr;
++ len = 32*1024;
++ } else if (ast->dp501_fw) {
++ fw_addr = (u8 *)ast->dp501_fw->data;
++ len = ast->dp501_fw->size;
++ }
++ /* Get BootAddress */
++ ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
++ data = ast_mindwm(ast, 0x1e6e0004);
++ switch (data & 0x03) {
++ case 0:
++ boot_address = 0x44000000;
++ break;
++ default:
++ case 1:
++ boot_address = 0x48000000;
++ break;
++ case 2:
++ boot_address = 0x50000000;
++ break;
++ case 3:
++ boot_address = 0x60000000;
++ break;
++ }
++ boot_address -= 0x200000; /* -2MB */
++
++ /* copy image to buffer */
++ for (i = 0; i < len; i += 4) {
++ data = *(u32 *)(fw_addr + i);
++ ast_moutdwm(ast, boot_address + i, data);
++ }
++
++ /* Init SCU */
++ ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
++
++ /* Launch FW */
++ ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address);
++ ast_moutdwm(ast, 0x1e6e2100, 1);
++
++ /* Update Scratch */
++ data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */
++ data |= 0x800;
++ ast_moutdwm(ast, 0x1e6e2040, data);
++
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
++ jreg |= 0x02;
++ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg);
++ }
++ return true;
++}
++
++u8 ast_get_dp501_max_clk(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u32 boot_address, offset, data;
++ u8 linkcap[4], linkrate, linklanes, maxclk = 0xff;
++
++ boot_address = get_fw_base(ast);
++
++ /* validate FW version */
++ offset = 0xf000;
++ data = ast_mindwm(ast, boot_address + offset);
++ if ((data & 0xf0) != 0x10) /* version: 1x */
++ return maxclk;
++
++ /* Read Link Capability */
++ offset = 0xf014;
++ data = ast_mindwm(ast, boot_address + offset);
++ linkcap[0] = (data & 0xff000000) >> 24;
++ linkcap[1] = (data & 0x00ff0000) >> 16;
++ linkcap[2] = (data & 0x0000ff00) >> 8;
++ linkcap[3] = (data & 0x000000ff);
++ if (linkcap[2] == 0) {
++ linkrate = linkcap[0];
++ linklanes = linkcap[1];
++ data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes);
++ if (data > 0xff)
++ data = 0xff;
++ maxclk = (u8)data;
++ }
++ return maxclk;
++}
++
++bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
++{
++ struct ast_private *ast = dev->dev_private;
++ u32 i, boot_address, offset, data;
++
++ boot_address = get_fw_base(ast);
++
++ /* validate FW version */
++ offset = 0xf000;
++ data = ast_mindwm(ast, boot_address + offset);
++ if ((data & 0xf0) != 0x10)
++ return false;
++
++ /* validate PnP Monitor */
++ offset = 0xf010;
++ data = ast_mindwm(ast, boot_address + offset);
++ if (!(data & 0x01))
++ return false;
++
++ /* Read EDID */
++ offset = 0xf020;
++ for (i = 0; i < 128; i += 4) {
++ data = ast_mindwm(ast, boot_address + offset + i);
++ *(u32 *)(ediddata + i) = data;
++ }
++
++ return true;
++}
++
++static bool ast_init_dvo(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 jreg;
++ u32 data;
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ ast_write32(ast, 0x12000, 0x1688a8a8);
++
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ if (!(jreg & 0x80)) {
++ /* Init SCU DVO Settings */
++ data = ast_read32(ast, 0x12008);
++ /* delay phase */
++ data &= 0xfffff8ff;
++ data |= 0x00000500;
++ ast_write32(ast, 0x12008, data);
++
++ if (ast->chip == AST2300) {
++ data = ast_read32(ast, 0x12084);
++ /* multi-pins for DVO single-edge */
++ data |= 0xfffe0000;
++ ast_write32(ast, 0x12084, data);
++
++ data = ast_read32(ast, 0x12088);
++ /* multi-pins for DVO single-edge */
++ data |= 0x000fffff;
++ ast_write32(ast, 0x12088, data);
++
++ data = ast_read32(ast, 0x12090);
++ /* multi-pins for DVO single-edge */
++ data &= 0xffffffcf;
++ data |= 0x00000020;
++ ast_write32(ast, 0x12090, data);
++ } else { /* AST2400 */
++ data = ast_read32(ast, 0x12088);
++ /* multi-pins for DVO single-edge */
++ data |= 0x30000000;
++ ast_write32(ast, 0x12088, data);
++
++ data = ast_read32(ast, 0x1208c);
++ /* multi-pins for DVO single-edge */
++ data |= 0x000000cf;
++ ast_write32(ast, 0x1208c, data);
++
++ data = ast_read32(ast, 0x120a4);
++ /* multi-pins for DVO single-edge */
++ data |= 0xffff0000;
++ ast_write32(ast, 0x120a4, data);
++
++ data = ast_read32(ast, 0x120a8);
++ /* multi-pins for DVO single-edge */
++ data |= 0x0000000f;
++ ast_write32(ast, 0x120a8, data);
++
++ data = ast_read32(ast, 0x12094);
++ /* multi-pins for DVO single-edge */
++ data |= 0x00000002;
++ ast_write32(ast, 0x12094, data);
++ }
++ }
++
++ /* Force to DVO */
++ data = ast_read32(ast, 0x1202c);
++ data &= 0xfffbffff;
++ ast_write32(ast, 0x1202c, data);
++
++ /* Init VGA DVO Settings */
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80);
++ return true;
++}
++
++
++static void ast_init_analog(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u32 data;
++
++ /*
++ * Set DAC source to VGA mode in SCU2C via the P2A
++ * bridge. First configure the P2U to target the SCU
++ * in case it isn't at this stage.
++ */
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++
++ /* Then unlock the SCU with the magic password */
++ ast_write32(ast, 0x12000, 0x1688a8a8);
++ ast_write32(ast, 0x12000, 0x1688a8a8);
++ ast_write32(ast, 0x12000, 0x1688a8a8);
++
++ /* Finally, clear bits [17:16] of SCU2c */
++ data = ast_read32(ast, 0x1202c);
++ data &= 0xfffcffff;
++ ast_write32(ast, 0, data);
++
++ /* Disable DVO */
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00);
++}
++
++void ast_init_3rdtx(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 jreg;
++
++ if (ast->chip == AST2300 || ast->chip == AST2400) {
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
++ switch (jreg & 0x0e) {
++ case 0x04:
++ ast_init_dvo(dev);
++ break;
++ case 0x08:
++ ast_launch_m68k(dev);
++ break;
++ case 0x0c:
++ ast_init_dvo(dev);
++ break;
++ default:
++ if (ast->tx_chip_type == AST_TX_SIL164)
++ ast_init_dvo(dev);
++ else
++ ast_init_analog(dev);
++ }
++ }
++}
+diff --git a/src/drivers/aspeed/common/ast_dram_tables.h b/src/drivers/aspeed/common/ast_dram_tables.h
+new file mode 100644
+index 0000000..4884cba
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_dram_tables.h
+@@ -0,0 +1,165 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * File taken from the Linux ast driver (v3.18.5)
++ * Coreboot-specific includes added at top and/or contents modified
++ * as needed to function within the coreboot environment.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc.
++ */
++
++#ifndef AST_DRAM_TABLES_H
++#define AST_DRAM_TABLES_H
++
++/* DRAM timing tables */
++struct ast_dramstruct {
++ u16 index;
++ u32 data;
++};
++
++static const struct ast_dramstruct ast2000_dram_table_data[] = {
++ { 0x0108, 0x00000000 },
++ { 0x0120, 0x00004a21 },
++ { 0xFF00, 0x00000043 },
++ { 0x0000, 0xFFFFFFFF },
++ { 0x0004, 0x00000089 },
++ { 0x0008, 0x22331353 },
++ { 0x000C, 0x0d07000b },
++ { 0x0010, 0x11113333 },
++ { 0x0020, 0x00110350 },
++ { 0x0028, 0x1e0828f0 },
++ { 0x0024, 0x00000001 },
++ { 0x001C, 0x00000000 },
++ { 0x0014, 0x00000003 },
++ { 0xFF00, 0x00000043 },
++ { 0x0018, 0x00000131 },
++ { 0x0014, 0x00000001 },
++ { 0xFF00, 0x00000043 },
++ { 0x0018, 0x00000031 },
++ { 0x0014, 0x00000001 },
++ { 0xFF00, 0x00000043 },
++ { 0x0028, 0x1e0828f1 },
++ { 0x0024, 0x00000003 },
++ { 0x002C, 0x1f0f28fb },
++ { 0x0030, 0xFFFFFE01 },
++ { 0xFFFF, 0xFFFFFFFF }
++};
++
++static const struct ast_dramstruct ast1100_dram_table_data[] = {
++ { 0x2000, 0x1688a8a8 },
++ { 0x2020, 0x000041f0 },
++ { 0xFF00, 0x00000043 },
++ { 0x0000, 0xfc600309 },
++ { 0x006C, 0x00909090 },
++ { 0x0064, 0x00050000 },
++ { 0x0004, 0x00000585 },
++ { 0x0008, 0x0011030f },
++ { 0x0010, 0x22201724 },
++ { 0x0018, 0x1e29011a },
++ { 0x0020, 0x00c82222 },
++ { 0x0014, 0x01001523 },
++ { 0x001C, 0x1024010d },
++ { 0x0024, 0x00cb2522 },
++ { 0x0038, 0xffffff82 },
++ { 0x003C, 0x00000000 },
++ { 0x0040, 0x00000000 },
++ { 0x0044, 0x00000000 },
++ { 0x0048, 0x00000000 },
++ { 0x004C, 0x00000000 },
++ { 0x0050, 0x00000000 },
++ { 0x0054, 0x00000000 },
++ { 0x0058, 0x00000000 },
++ { 0x005C, 0x00000000 },
++ { 0x0060, 0x032aa02a },
++ { 0x0064, 0x002d3000 },
++ { 0x0068, 0x00000000 },
++ { 0x0070, 0x00000000 },
++ { 0x0074, 0x00000000 },
++ { 0x0078, 0x00000000 },
++ { 0x007C, 0x00000000 },
++ { 0x0034, 0x00000001 },
++ { 0xFF00, 0x00000043 },
++ { 0x002C, 0x00000732 },
++ { 0x0030, 0x00000040 },
++ { 0x0028, 0x00000005 },
++ { 0x0028, 0x00000007 },
++ { 0x0028, 0x00000003 },
++ { 0x0028, 0x00000001 },
++ { 0x000C, 0x00005a08 },
++ { 0x002C, 0x00000632 },
++ { 0x0028, 0x00000001 },
++ { 0x0030, 0x000003c0 },
++ { 0x0028, 0x00000003 },
++ { 0x0030, 0x00000040 },
++ { 0x0028, 0x00000003 },
++ { 0x000C, 0x00005a21 },
++ { 0x0034, 0x00007c03 },
++ { 0x0120, 0x00004c41 },
++ { 0xffff, 0xffffffff },
++};
++
++static const struct ast_dramstruct ast2100_dram_table_data[] = {
++ { 0x2000, 0x1688a8a8 },
++ { 0x2020, 0x00004120 },
++ { 0xFF00, 0x00000043 },
++ { 0x0000, 0xfc600309 },
++ { 0x006C, 0x00909090 },
++ { 0x0064, 0x00070000 },
++ { 0x0004, 0x00000489 },
++ { 0x0008, 0x0011030f },
++ { 0x0010, 0x32302926 },
++ { 0x0018, 0x274c0122 },
++ { 0x0020, 0x00ce2222 },
++ { 0x0014, 0x01001523 },
++ { 0x001C, 0x1024010d },
++ { 0x0024, 0x00cb2522 },
++ { 0x0038, 0xffffff82 },
++ { 0x003C, 0x00000000 },
++ { 0x0040, 0x00000000 },
++ { 0x0044, 0x00000000 },
++ { 0x0048, 0x00000000 },
++ { 0x004C, 0x00000000 },
++ { 0x0050, 0x00000000 },
++ { 0x0054, 0x00000000 },
++ { 0x0058, 0x00000000 },
++ { 0x005C, 0x00000000 },
++ { 0x0060, 0x0f2aa02a },
++ { 0x0064, 0x003f3005 },
++ { 0x0068, 0x02020202 },
++ { 0x0070, 0x00000000 },
++ { 0x0074, 0x00000000 },
++ { 0x0078, 0x00000000 },
++ { 0x007C, 0x00000000 },
++ { 0x0034, 0x00000001 },
++ { 0xFF00, 0x00000043 },
++ { 0x002C, 0x00000942 },
++ { 0x0030, 0x00000040 },
++ { 0x0028, 0x00000005 },
++ { 0x0028, 0x00000007 },
++ { 0x0028, 0x00000003 },
++ { 0x0028, 0x00000001 },
++ { 0x000C, 0x00005a08 },
++ { 0x002C, 0x00000842 },
++ { 0x0028, 0x00000001 },
++ { 0x0030, 0x000003c0 },
++ { 0x0028, 0x00000003 },
++ { 0x0030, 0x00000040 },
++ { 0x0028, 0x00000003 },
++ { 0x000C, 0x00005a21 },
++ { 0x0034, 0x00007c03 },
++ { 0x0120, 0x00005061 },
++ { 0xffff, 0xffffffff },
++};
++
++#endif
+diff --git a/src/drivers/aspeed/common/ast_drv.h b/src/drivers/aspeed/common/ast_drv.h
+new file mode 100644
+index 0000000..53640f1
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_drv.h
+@@ -0,0 +1,223 @@
++/*
++ * Copyright 2012 Red Hat Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ */
++/*
++ * Authors: Dave Airlie <airlied@redhat.com>
++ */
++#ifndef __AST_DRV_H__
++#define __AST_DRV_H__
++
++#include "aspeed_coreboot.h"
++
++#define PCI_CHIP_AST2000 0x2000
++#define PCI_CHIP_AST2100 0x2010
++#define PCI_CHIP_AST1180 0x1180
++
++
++enum ast_chip {
++ AST2000,
++ AST2100,
++ AST1100,
++ AST2200,
++ AST2150,
++ AST2300,
++ AST2400,
++ AST1180,
++};
++
++enum ast_tx_chip {
++ AST_TX_NONE,
++ AST_TX_SIL164,
++ AST_TX_ITE66121,
++ AST_TX_DP501,
++};
++
++#define AST_DRAM_512Mx16 0
++#define AST_DRAM_1Gx16 1
++#define AST_DRAM_512Mx32 2
++#define AST_DRAM_1Gx32 3
++#define AST_DRAM_2Gx16 6
++#define AST_DRAM_4Gx16 7
++
++struct ast_fbdev;
++
++struct ast_private {
++ struct drm_device *dev;
++
++ void __iomem *regs;
++ void __iomem *ioregs;
++ bool io_space_uses_mmap;
++
++ enum ast_chip chip;
++ bool vga2_clone;
++ uint32_t dram_bus_width;
++ uint32_t dram_type;
++ uint32_t mclk;
++ uint32_t vram_size;
++
++ struct ast_fbdev *fbdev;
++
++ int fb_mtrr;
++
++ struct drm_gem_object *cursor_cache;
++ uint64_t cursor_cache_gpu_addr;
++
++ int next_cursor;
++ bool support_wide_screen;
++
++ enum ast_tx_chip tx_chip_type;
++ u8 dp501_maxclk;
++ u8 *dp501_fw_addr;
++ const struct firmware *dp501_fw; /* dp501 fw */
++};
++
++int ast_driver_load(struct drm_device *dev, unsigned long flags);
++int ast_driver_unload(struct drm_device *dev);
++
++#define AST_IO_AR_PORT_WRITE (0x40)
++#define AST_IO_MISC_PORT_WRITE (0x42)
++#define AST_IO_VGA_ENABLE_PORT (0x43)
++#define AST_IO_SEQ_PORT (0x44)
++#define AST_IO_DAC_INDEX_READ (0x47)
++#define AST_IO_DAC_INDEX_WRITE (0x48)
++#define AST_IO_DAC_DATA (0x49)
++#define AST_IO_GR_PORT (0x4E)
++#define AST_IO_CRTC_PORT (0x54)
++#define AST_IO_INPUT_STATUS1_READ (0x5A)
++#define AST_IO_MISC_PORT_READ (0x4C)
++
++#define AST_IO_MM_OFFSET (0x380)
++
++#define __ast_read(x) \
++static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \
++u##x val = 0;\
++val = ioread##x(ast->regs + reg); \
++return val;\
++}
++
++__ast_read(8);
++__ast_read(16);
++__ast_read(32)
++
++#define __ast_io_read(x) \
++static inline u##x ast_io_read##x(struct ast_private *ast, u32 reg) { \
++u##x val = 0;\
++if (ast->io_space_uses_mmap) \
++val = ioread##x(ast->regs + reg); \
++else \
++val = ioread_cbio##x(ast->ioregs + reg); \
++return val;\
++}
++
++__ast_io_read(8);
++__ast_io_read(16);
++__ast_io_read(32);
++
++#define __ast_write(x) \
++static inline void ast_write##x(struct ast_private *ast, u32 reg, u##x val) {\
++ iowrite##x(val, ast->regs + reg);\
++ }
++
++__ast_write(8);
++__ast_write(16);
++__ast_write(32);
++
++#define __ast_io_write(x) \
++static inline void ast_io_write##x(struct ast_private *ast, u32 reg, u##x val) {\
++ if (ast->io_space_uses_mmap) \
++ iowrite##x(val, ast->regs + reg);\
++ else \
++ iowrite_cbio##x(val, ast->ioregs + reg);\
++ }
++
++__ast_io_write(8);
++__ast_io_write(16);
++#undef __ast_io_write
++
++static inline void ast_set_index_reg(struct ast_private *ast,
++ uint32_t base, uint8_t index,
++ uint8_t val)
++{
++ ast_io_write16(ast, base, ((u16)val << 8) | index);
++}
++
++void ast_set_index_reg_mask(struct ast_private *ast,
++ uint32_t base, uint8_t index,
++ uint8_t mask, uint8_t val);
++uint8_t ast_get_index_reg(struct ast_private *ast,
++ uint32_t base, uint8_t index);
++uint8_t ast_get_index_reg_mask(struct ast_private *ast,
++ uint32_t base, uint8_t index, uint8_t mask);
++
++static inline void ast_open_key(struct ast_private *ast)
++{
++ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8);
++}
++
++#define AST_VIDMEM_SIZE_8M 0x00800000
++#define AST_VIDMEM_SIZE_16M 0x01000000
++#define AST_VIDMEM_SIZE_32M 0x02000000
++#define AST_VIDMEM_SIZE_64M 0x04000000
++#define AST_VIDMEM_SIZE_128M 0x08000000
++
++#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M
++
++#define AST_MAX_HWC_WIDTH 64
++#define AST_MAX_HWC_HEIGHT 64
++
++#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH*AST_MAX_HWC_HEIGHT*2)
++#define AST_HWC_SIGNATURE_SIZE 32
++
++#define AST_DEFAULT_HWC_NUM 2
++/* define for signature structure */
++#define AST_HWC_SIGNATURE_CHECKSUM 0x00
++#define AST_HWC_SIGNATURE_SizeX 0x04
++#define AST_HWC_SIGNATURE_SizeY 0x08
++#define AST_HWC_SIGNATURE_X 0x0C
++#define AST_HWC_SIGNATURE_Y 0x10
++#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
++#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
++
++#define AST_MM_ALIGN_SHIFT 4
++#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1)
++
++#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
++
++/* ast post */
++void ast_enable_vga(struct drm_device *dev);
++void ast_enable_mmio(struct drm_device *dev);
++bool ast_is_vga_enabled(struct drm_device *dev);
++void ast_post_gpu(struct drm_device *dev);
++u32 ast_mindwm(struct ast_private *ast, u32 r);
++void ast_moutdwm(struct ast_private *ast, u32 r, u32 v);
++/* ast dp501 */
++int ast_load_dp501_microcode(struct drm_device *dev);
++void ast_set_dp501_video_output(struct drm_device *dev, u8 mode);
++bool ast_launch_m68k(struct drm_device *dev);
++bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
++bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
++u8 ast_get_dp501_max_clk(struct drm_device *dev);
++void ast_init_3rdtx(struct drm_device *dev);
++#endif
+diff --git a/src/drivers/aspeed/common/ast_main.c b/src/drivers/aspeed/common/ast_main.c
+new file mode 100644
+index 0000000..2939442
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_main.c
+@@ -0,0 +1,393 @@
++/*
++ * Copyright 2012 Red Hat Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ */
++/*
++ * Authors: Dave Airlie <airlied@redhat.com>
++ */
++#include "ast_drv.h"
++
++#include "ast_dram_tables.h"
++
++void ast_set_index_reg_mask(struct ast_private *ast,
++ uint32_t base, uint8_t index,
++ uint8_t mask, uint8_t val)
++{
++ u8 tmp;
++ ast_io_write8(ast, base, index);
++ tmp = (ast_io_read8(ast, base + 1) & mask) | val;
++ ast_set_index_reg(ast, base, index, tmp);
++}
++
++uint8_t ast_get_index_reg(struct ast_private *ast,
++ uint32_t base, uint8_t index)
++{
++ uint8_t ret;
++ ast_io_write8(ast, base, index);
++ ret = ast_io_read8(ast, base + 1);
++ return ret;
++}
++
++uint8_t ast_get_index_reg_mask(struct ast_private *ast,
++ uint32_t base, uint8_t index, uint8_t mask)
++{
++ uint8_t ret;
++ ast_io_write8(ast, base, index);
++ ret = ast_io_read8(ast, base + 1) & mask;
++ return ret;
++}
++
++
++static int ast_detect_chip(struct drm_device *dev, bool *need_post)
++{
++ struct ast_private *ast = dev->dev_private;
++ uint32_t data, jreg;
++ ast_open_key(ast);
++
++ if (dev->pdev->device == PCI_CHIP_AST1180) {
++ ast->chip = AST1100;
++ DRM_INFO("AST 1180 detected\n");
++ } else {
++ pci_read_config_dword(ast->dev->pdev, 0x08, &data);
++ uint8_t revision = data & 0xff;
++
++ if (revision >= 0x30) {
++ ast->chip = AST2400;
++ DRM_INFO("AST 2400 detected\n");
++ } else if (revision >= 0x20) {
++ ast->chip = AST2300;
++ DRM_INFO("AST 2300 detected\n");
++ } else if (revision >= 0x10) {
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++
++ data = ast_read32(ast, 0x1207c);
++ switch (data & 0x0300) {
++ case 0x0200:
++ ast->chip = AST1100;
++ DRM_INFO("AST 1100 detected\n");
++ break;
++ case 0x0100:
++ ast->chip = AST2200;
++ DRM_INFO("AST 2200 detected\n");
++ break;
++ case 0x0000:
++ ast->chip = AST2150;
++ DRM_INFO("AST 2150 detected\n");
++ break;
++ default:
++ ast->chip = AST2100;
++ DRM_INFO("AST 2100 detected\n");
++ break;
++ }
++ ast->vga2_clone = false;
++ } else {
++ ast->chip = AST2000;
++ DRM_INFO("AST 2000 detected\n");
++ }
++ }
++
++ /*
++ * If VGA isn't enabled, we need to enable now or subsequent
++ * access to the scratch registers will fail. We also inform
++ * our caller that it needs to POST the chip
++ * (Assumption: VGA not enabled -> need to POST)
++ */
++ if (!ast_is_vga_enabled(dev)) {
++ ast_enable_vga(dev);
++ ast_enable_mmio(dev);
++ DRM_INFO("VGA not enabled on entry, requesting chip POST\n");
++ *need_post = true;
++ } else
++ *need_post = false;
++
++ /* Check if we support wide screen */
++ switch (ast->chip) {
++ case AST1180:
++ ast->support_wide_screen = true;
++ break;
++ case AST2000:
++ ast->support_wide_screen = false;
++ break;
++ default:
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ if (!(jreg & 0x80))
++ ast->support_wide_screen = true;
++ else if (jreg & 0x01)
++ ast->support_wide_screen = true;
++ else {
++ ast->support_wide_screen = false;
++ /* Read SCU7c (silicon revision register) */
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ data = ast_read32(ast, 0x1207c);
++ data &= 0x300;
++ if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
++ ast->support_wide_screen = true;
++ if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
++ ast->support_wide_screen = true;
++ }
++ break;
++ }
++
++ /* Check 3rd Tx option (digital output afaik) */
++ ast->tx_chip_type = AST_TX_NONE;
++
++ /*
++ * VGACRA3 Enhanced Color Mode Register, check if DVO is already
++ * enabled, in that case, assume we have a SIL164 TMDS transmitter
++ *
++ * Don't make that assumption if we the chip wasn't enabled and
++ * is at power-on reset, otherwise we'll incorrectly "detect" a
++ * SIL164 when there is none.
++ */
++ if (!*need_post) {
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff);
++ if (jreg & 0x80)
++ ast->tx_chip_type = AST_TX_SIL164;
++ }
++
++ if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
++ /*
++ * On AST2300 and 2400, look the configuration set by the SoC in
++ * the SOC scratch register #1 bits 11:8 (interestingly marked
++ * as "reserved" in the spec)
++ */
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
++ switch (jreg) {
++ case 0x04:
++ ast->tx_chip_type = AST_TX_SIL164;
++ break;
++ case 0x08:
++ ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL);
++ if (ast->dp501_fw_addr) {
++ /* backup firmware */
++ if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
++ kfree(ast->dp501_fw_addr);
++ ast->dp501_fw_addr = NULL;
++ }
++ }
++ /* fallthrough */
++ case 0x0c:
++ ast->tx_chip_type = AST_TX_DP501;
++ }
++ }
++
++ /* Print stuff for diagnostic purposes */
++ switch(ast->tx_chip_type) {
++ case AST_TX_SIL164:
++ DRM_INFO("Using Sil164 TMDS transmitter\n");
++ break;
++ case AST_TX_DP501:
++ DRM_INFO("Using DP501 DisplayPort transmitter\n");
++ break;
++ default:
++ DRM_INFO("Analog VGA only\n");
++ }
++ return 0;
++}
++
++static int ast_get_dram_info(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ uint8_t i;
++ uint32_t data, data2;
++ uint32_t denum, num, div, ref_pll;
++
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++
++
++ ast_write32(ast, 0x10000, 0xfc600309);
++
++ /* Wait up to 2.5 seconds for device initialization / register unlock */
++ for (i = 0; i < 250; i++) {
++ if (ast_read32(ast, 0x10000) == 0x01)
++ break;
++ mdelay(10);
++ }
++ if (ast_read32(ast, 0x10000) != 0x01)
++ dev_err(dev->pdev, "Unable to unlock SDRAM control registers\n");
++
++ data = ast_read32(ast, 0x10004);
++
++ if (data & 0x400)
++ ast->dram_bus_width = 16;
++ else
++ ast->dram_bus_width = 32;
++
++ if (ast->chip == AST2300 || ast->chip == AST2400) {
++ switch (data & 0x03) {
++ case 0:
++ ast->dram_type = AST_DRAM_512Mx16;
++ break;
++ default:
++ case 1:
++ ast->dram_type = AST_DRAM_1Gx16;
++ break;
++ case 2:
++ ast->dram_type = AST_DRAM_2Gx16;
++ break;
++ case 3:
++ ast->dram_type = AST_DRAM_4Gx16;
++ break;
++ }
++ } else {
++ switch (data & 0x0c) {
++ case 0:
++ case 4:
++ ast->dram_type = AST_DRAM_512Mx16;
++ break;
++ case 8:
++ if (data & 0x40)
++ ast->dram_type = AST_DRAM_1Gx16;
++ else
++ ast->dram_type = AST_DRAM_512Mx32;
++ break;
++ case 0xc:
++ ast->dram_type = AST_DRAM_1Gx32;
++ break;
++ }
++ }
++
++ data = ast_read32(ast, 0x10120);
++ data2 = ast_read32(ast, 0x10170);
++ if (data2 & 0x2000)
++ ref_pll = 14318;
++ else
++ ref_pll = 12000;
++
++ denum = data & 0x1f;
++ num = (data & 0x3fe0) >> 5;
++ data = (data & 0xc000) >> 14;
++ switch (data) {
++ case 3:
++ div = 0x4;
++ break;
++ case 2:
++ case 1:
++ div = 0x2;
++ break;
++ default:
++ div = 0x1;
++ break;
++ }
++ ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
++ return 0;
++}
++
++static u32 ast_get_vram_info(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 jreg;
++ u32 vram_size;
++ ast_open_key(ast);
++
++ vram_size = AST_VIDMEM_DEFAULT_SIZE;
++ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
++ switch (jreg & 3) {
++ case 0: vram_size = AST_VIDMEM_SIZE_8M; break;
++ case 1: vram_size = AST_VIDMEM_SIZE_16M; break;
++ case 2: vram_size = AST_VIDMEM_SIZE_32M; break;
++ case 3: vram_size = AST_VIDMEM_SIZE_64M; break;
++ }
++
++ return vram_size;
++}
++
++int ast_driver_load(struct drm_device *dev, unsigned long flags)
++{
++ struct ast_private *ast;
++ bool need_post;
++ int ret = 0;
++ struct resource *res;
++
++ ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL);
++ if (!ast)
++ return -ENOMEM;
++
++ dev->dev_private = ast;
++ ast->dev = dev;
++
++ /* PCI BAR 1 */
++ res = find_resource(dev->pdev, 0x14);
++ if (!res) {
++ dev_err(dev->pdev, "BAR1 resource not found.\n");
++ ret = -EIO;
++ goto out_free;
++ }
++ ast->regs = res2mmio(res, 0, 0);
++ if (!ast->regs) {
++ ret = -EIO;
++ goto out_free;
++ }
++
++ /* PCI BAR 2 */
++ ast->io_space_uses_mmap = false;
++ res = find_resource(dev->pdev, 0x18);
++ if (!res) {
++ dev_err(dev->pdev, "BAR2 resource not found.\n");
++ ret = -EIO;
++ goto out_free;
++ }
++
++ /*
++ * If we don't have IO space at all, use MMIO now and
++ * assume the chip has MMIO enabled by default (rev 0x20
++ * and higher).
++ */
++ if (!(res->flags & IORESOURCE_IO)) {
++ DRM_INFO("platform has no IO space, trying MMIO\n");
++ ast->ioregs = ast->regs + AST_IO_MM_OFFSET;
++ ast->io_space_uses_mmap = true;
++ }
++
++ /* "map" IO regs if the above hasn't done so already */
++ if (!ast->ioregs) {
++ ast->ioregs = res2mmio(res, 0, 0);
++ if (!ast->ioregs) {
++ ret = -EIO;
++ goto out_free;
++ }
++ /* Adjust the I/O space location to match expectations (the code expects offset 0x0 to be I/O location 0x380) */
++ ast->ioregs = (void *)AST_IO_MM_OFFSET;
++ }
++
++ ast_detect_chip(dev, &need_post);
++
++ if (ast->chip != AST1180) {
++ ast_get_dram_info(dev);
++ ast->vram_size = ast_get_vram_info(dev);
++ DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
++ }
++
++ if (need_post)
++ ast_post_gpu(dev);
++
++ return 0;
++out_free:
++ kfree(ast);
++ dev->dev_private = NULL;
++ return ret;
++}
+diff --git a/src/drivers/aspeed/common/ast_post.c b/src/drivers/aspeed/common/ast_post.c
+new file mode 100644
+index 0000000..7d31845
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_post.c
+@@ -0,0 +1,1679 @@
++/*
++ * Copyright 2012 Red Hat Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ */
++/*
++ * Authors: Dave Airlie <airlied@redhat.com>
++ */
++
++#include "ast_drv.h"
++
++#include "ast_dram_tables.h"
++
++static void ast_init_dram_2300(struct drm_device *dev);
++
++void ast_enable_vga(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++
++ ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01);
++ ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01);
++}
++
++void ast_enable_mmio(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04);
++}
++
++
++bool ast_is_vga_enabled(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 ch;
++
++ if (ast->chip == AST1180) {
++ /* TODO 1180 */
++ } else {
++ ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT);
++ if (ch) {
++ ast_open_key(ast);
++ ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
++ return ch & 0x04;
++ }
++ }
++ return 0;
++}
++
++static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
++static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff };
++static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff };
++
++static void
++ast_set_def_ext_reg(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 i, index, reg;
++ uint32_t data;
++ const u8 *ext_reg_info;
++
++ pci_read_config_dword(ast->dev->pdev, 0x08, &data);
++ uint8_t revision = data & 0xff;
++
++ /* reset scratch */
++ for (i = 0x81; i <= 0x8f; i++)
++ ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00);
++
++ if (ast->chip == AST2300 || ast->chip == AST2400) {
++ if (revision >= 0x20)
++ ext_reg_info = extreginfo_ast2300;
++ else
++ ext_reg_info = extreginfo_ast2300a0;
++ } else
++ ext_reg_info = extreginfo;
++
++ index = 0xa0;
++ while (*ext_reg_info != 0xff) {
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, index, 0x00, *ext_reg_info);
++ index++;
++ ext_reg_info++;
++ }
++
++ /* disable standard IO/MEM decode if secondary */
++ /* ast_set_index_reg-mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x3); */
++
++ /* Set Ext. Default */
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x8c, 0x00, 0x01);
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x00, 0x00);
++
++ /* Enable RAMDAC for A1 */
++ reg = 0x04;
++ if (ast->chip == AST2300 || ast->chip == AST2400)
++ reg |= 0x20;
++ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg);
++}
++
++u32 ast_mindwm(struct ast_private *ast, u32 r)
++{
++ uint32_t data;
++
++ ast_write32(ast, 0xf004, r & 0xffff0000);
++ ast_write32(ast, 0xf000, 0x1);
++
++ do {
++ data = ast_read32(ast, 0xf004) & 0xffff0000;
++ } while (data != (r & 0xffff0000));
++ return ast_read32(ast, 0x10000 + (r & 0x0000ffff));
++}
++
++void ast_moutdwm(struct ast_private *ast, u32 r, u32 v)
++{
++ uint32_t data;
++ ast_write32(ast, 0xf004, r & 0xffff0000);
++ ast_write32(ast, 0xf000, 0x1);
++ do {
++ data = ast_read32(ast, 0xf004) & 0xffff0000;
++ } while (data != (r & 0xffff0000));
++ ast_write32(ast, 0x10000 + (r & 0x0000ffff), v);
++}
++
++/*
++ * AST2100/2150 DLL CBR Setting
++ */
++#define CBR_SIZE_AST2150 ((16 << 10) - 1)
++#define CBR_PASSNUM_AST2150 5
++#define CBR_THRESHOLD_AST2150 10
++#define CBR_THRESHOLD2_AST2150 10
++#define TIMEOUT_AST2150 5000000
++
++#define CBR_PATNUM_AST2150 8
++
++static const u32 pattern_AST2150[14] = {
++ 0xFF00FF00,
++ 0xCC33CC33,
++ 0xAA55AA55,
++ 0xFFFE0001,
++ 0x683501FE,
++ 0x0F1929B0,
++ 0x2D0B4346,
++ 0x60767F02,
++ 0x6FBE36A6,
++ 0x3A253035,
++ 0x3019686D,
++ 0x41C6167E,
++ 0x620152BF,
++ 0x20F050E0
++};
++
++static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
++ if (++timeout > TIMEOUT_AST2150) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return 0xffffffff;
++ }
++ } while (!data);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
++ if (++timeout > TIMEOUT_AST2150) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return 0xffffffff;
++ }
++ } while (!data);
++ data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return data;
++}
++
++#if 0 /* unused in DDX driver - here for completeness */
++static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
++ if (++timeout > TIMEOUT_AST2150) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return 0xffffffff;
++ }
++ } while (!data);
++ data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return data;
++}
++#endif
++
++static int cbrtest_ast2150(struct ast_private *ast)
++{
++ int i;
++
++ for (i = 0; i < 8; i++)
++ if (mmctestburst2_ast2150(ast, i))
++ return 0;
++ return 1;
++}
++
++static int cbrscan_ast2150(struct ast_private *ast, int busw)
++{
++ u32 patcnt, loop;
++
++ for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
++ ast_moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
++ for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
++ if (cbrtest_ast2150(ast))
++ break;
++ }
++ if (loop == CBR_PASSNUM_AST2150)
++ return 0;
++ }
++ return 1;
++}
++
++
++static void cbrdlli_ast2150(struct ast_private *ast, int busw)
++{
++ u32 dll_min[4], dll_max[4], dlli, data, passcnt;
++
++cbr_start:
++ dll_min[0] = dll_min[1] = dll_min[2] = dll_min[3] = 0xff;
++ dll_max[0] = dll_max[1] = dll_max[2] = dll_max[3] = 0x0;
++ passcnt = 0;
++
++ for (dlli = 0; dlli < 100; dlli++) {
++ ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
++ data = cbrscan_ast2150(ast, busw);
++ if (data != 0) {
++ if (data & 0x1) {
++ if (dll_min[0] > dlli)
++ dll_min[0] = dlli;
++ if (dll_max[0] < dlli)
++ dll_max[0] = dlli;
++ }
++ passcnt++;
++ } else if (passcnt >= CBR_THRESHOLD_AST2150)
++ goto cbr_start;
++ }
++ if (dll_max[0] == 0 || (dll_max[0]-dll_min[0]) < CBR_THRESHOLD_AST2150)
++ goto cbr_start;
++
++ dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
++ ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
++}
++
++
++
++static void ast_init_dram_reg(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ u8 j;
++ u32 data, temp, i;
++ const struct ast_dramstruct *dram_reg_info;
++
++ j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++
++ if ((j & 0x80) == 0) { /* VGA only */
++ if (ast->chip == AST2000) {
++ dram_reg_info = ast2000_dram_table_data;
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ ast_write32(ast, 0x10100, 0xa8);
++
++ do {
++ ;
++ } while (ast_read32(ast, 0x10100) != 0xa8);
++ } else {/* AST2100/1100 */
++ if (ast->chip == AST2100 || ast->chip == 2200)
++ dram_reg_info = ast2100_dram_table_data;
++ else
++ dram_reg_info = ast1100_dram_table_data;
++
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ ast_write32(ast, 0x12000, 0x1688A8A8);
++
++ /* Wait up to 2.5 seconds for device initialization / register unlock */
++ for (i = 0; i < 250; i++) {
++ if (ast_read32(ast, 0x12000) == 0x01)
++ break;
++ mdelay(10);
++ }
++ if (ast_read32(ast, 0x12000) != 0x01)
++ dev_err(dev->pdev, "Unable to unlock SCU registers\n");
++
++ ast_write32(ast, 0x10000, 0xfc600309);
++
++ /* Wait up to 2.5 seconds for device initialization / register unlock */
++ for (i = 0; i < 250; i++) {
++ if (ast_read32(ast, 0x10000) == 0x01)
++ break;
++ mdelay(10);
++ }
++ if (ast_read32(ast, 0x10000) != 0x01)
++ dev_err(dev->pdev, "Unable to unlock SDRAM control registers\n");
++ }
++
++ while (dram_reg_info->index != 0xffff) {
++ if (dram_reg_info->index == 0xff00) {/* delay fn */
++ for (i = 0; i < 15; i++)
++ udelay(dram_reg_info->data);
++ } else if (dram_reg_info->index == 0x4 && ast->chip != AST2000) {
++ data = dram_reg_info->data;
++ if (ast->dram_type == AST_DRAM_1Gx16)
++ data = 0x00000d89;
++ else if (ast->dram_type == AST_DRAM_1Gx32)
++ data = 0x00000c8d;
++
++ temp = ast_read32(ast, 0x12070);
++ temp &= 0xc;
++ temp <<= 2;
++ ast_write32(ast, 0x10000 + dram_reg_info->index, data | temp);
++ } else
++ ast_write32(ast, 0x10000 + dram_reg_info->index, dram_reg_info->data);
++ dram_reg_info++;
++ }
++
++ /* AST 2100/2150 DRAM calibration */
++ data = ast_read32(ast, 0x10120);
++ if (data == 0x5061) { /* 266Mhz */
++ data = ast_read32(ast, 0x10004);
++ if (data & 0x40)
++ cbrdlli_ast2150(ast, 16); /* 16 bits */
++ else
++ cbrdlli_ast2150(ast, 32); /* 32 bits */
++ }
++
++ switch (ast->chip) {
++ case AST2000:
++ temp = ast_read32(ast, 0x10140);
++ ast_write32(ast, 0x10140, temp | 0x40);
++ break;
++ case AST1100:
++ case AST2100:
++ case AST2200:
++ case AST2150:
++ temp = ast_read32(ast, 0x1200c);
++ ast_write32(ast, 0x1200c, temp & 0xfffffffd);
++ temp = ast_read32(ast, 0x12040);
++ ast_write32(ast, 0x12040, temp | 0x40);
++ break;
++ default:
++ break;
++ }
++ }
++
++ /* wait ready */
++ /* Wait up to 2.5 seconds for device to become ready */
++ for (i = 0; i < 250; i++) {
++ j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ mdelay(10);
++ if ((j & 0x40) != 0)
++ break;
++ }
++ if ((j & 0x40) == 0)
++ dev_err(dev->pdev, "Timeout while waiting for device to signal ready\n");
++}
++
++void ast_post_gpu(struct drm_device *dev)
++{
++ u32 reg;
++ struct ast_private *ast = dev->dev_private;
++
++ pci_read_config_dword(ast->dev->pdev, 0x04, &reg);
++ reg |= 0x3;
++ pci_write_config_dword(ast->dev->pdev, 0x04, reg);
++
++ ast_enable_vga(dev);
++ ast_enable_mmio(dev);
++ ast_open_key(ast);
++ ast_set_def_ext_reg(dev);
++
++ if (ast->chip == AST2300 || ast->chip == AST2400)
++ ast_init_dram_2300(dev);
++ else
++ ast_init_dram_reg(dev);
++
++ ast_init_3rdtx(dev);
++}
++
++/* AST 2300 DRAM settings */
++#define AST_DDR3 0
++#define AST_DDR2 1
++
++struct ast2300_dram_param {
++ u32 dram_type;
++ u32 dram_chipid;
++ u32 dram_freq;
++ u32 vram_size;
++ u32 odt;
++ u32 wodt;
++ u32 rodt;
++ u32 dram_config;
++ u32 reg_PERIOD;
++ u32 reg_MADJ;
++ u32 reg_SADJ;
++ u32 reg_MRS;
++ u32 reg_EMRS;
++ u32 reg_AC1;
++ u32 reg_AC2;
++ u32 reg_DQSIC;
++ u32 reg_DRV;
++ u32 reg_IOZ;
++ u32 reg_DQIDLY;
++ u32 reg_FREQ;
++ u32 madj_max;
++ u32 dll2_finetune_step;
++};
++
++/*
++ * DQSI DLL CBR Setting
++ */
++#define CBR_SIZE0 ((1 << 10) - 1)
++#define CBR_SIZE1 ((4 << 10) - 1)
++#define CBR_SIZE2 ((64 << 10) - 1)
++#define CBR_PASSNUM 5
++#define CBR_PASSNUM2 5
++#define CBR_THRESHOLD 10
++#define CBR_THRESHOLD2 10
++#define TIMEOUT 5000000
++#define CBR_PATNUM 8
++
++static const u32 pattern[8] = {
++ 0xFF00FF00,
++ 0xCC33CC33,
++ 0xAA55AA55,
++ 0x88778877,
++ 0x92CC4D6E,
++ 0x543D3CDE,
++ 0xF1E843C7,
++ 0x7C61D253
++};
++
++static int mmc_test_burst(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
++ if (data & 0x2000) {
++ return 0;
++ }
++ if (++timeout > TIMEOUT) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return 0;
++ }
++ } while (!data);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ return 1;
++}
++
++static int mmc_test_burst2(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
++ if (++timeout > TIMEOUT) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return -1;
++ }
++ } while (!data);
++ data = ast_mindwm(ast, 0x1e6e0078);
++ data = (data | (data >> 16)) & 0xffff;
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return data;
++}
++
++static int mmc_test_single(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
++ if (data & 0x2000)
++ return 0;
++ if (++timeout > TIMEOUT) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return 0;
++ }
++ } while (!data);
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return 1;
++}
++
++static int mmc_test_single2(struct ast_private *ast, u32 datagen)
++{
++ u32 data, timeout;
++
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
++ ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
++ timeout = 0;
++ do {
++ data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
++ if (++timeout > TIMEOUT) {
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return -1;
++ }
++ } while (!data);
++ data = ast_mindwm(ast, 0x1e6e0078);
++ data = (data | (data >> 16)) & 0xffff;
++ ast_moutdwm(ast, 0x1e6e0070, 0x0);
++ return data;
++}
++
++static int cbr_test(struct ast_private *ast)
++{
++ u32 data;
++ int i;
++ data = mmc_test_single2(ast, 0);
++ if ((data & 0xff) && (data & 0xff00))
++ return 0;
++ for (i = 0; i < 8; i++) {
++ data = mmc_test_burst2(ast, i);
++ if ((data & 0xff) && (data & 0xff00))
++ return 0;
++ }
++ if (!data)
++ return 3;
++ else if (data & 0xff)
++ return 2;
++ return 1;
++}
++
++static int cbr_scan(struct ast_private *ast)
++{
++ u32 data, data2, patcnt, loop;
++
++ data2 = 3;
++ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
++ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
++ for (loop = 0; loop < CBR_PASSNUM2; loop++) {
++ if ((data = cbr_test(ast)) != 0) {
++ data2 &= data;
++ if (!data2)
++ return 0;
++ break;
++ }
++ }
++ if (loop == CBR_PASSNUM2)
++ return 0;
++ }
++ return data2;
++}
++
++static u32 cbr_test2(struct ast_private *ast)
++{
++ u32 data;
++
++ data = mmc_test_burst2(ast, 0);
++ if (data == 0xffff)
++ return 0;
++ data |= mmc_test_single2(ast, 0);
++ if (data == 0xffff)
++ return 0;
++
++ return ~data & 0xffff;
++}
++
++static u32 cbr_scan2(struct ast_private *ast)
++{
++ u32 data, data2, patcnt, loop;
++
++ data2 = 0xffff;
++ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
++ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
++ for (loop = 0; loop < CBR_PASSNUM2; loop++) {
++ if ((data = cbr_test2(ast)) != 0) {
++ data2 &= data;
++ if (!data2)
++ return 0;
++ break;
++ }
++ }
++ if (loop == CBR_PASSNUM2)
++ return 0;
++ }
++ return data2;
++}
++
++static u32 cbr_test3(struct ast_private *ast)
++{
++ if (!mmc_test_burst(ast, 0))
++ return 0;
++ if (!mmc_test_single(ast, 0))
++ return 0;
++ return 1;
++}
++
++static u32 cbr_scan3(struct ast_private *ast)
++{
++ u32 patcnt, loop;
++
++ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
++ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
++ for (loop = 0; loop < 2; loop++) {
++ if (cbr_test3(ast))
++ break;
++ }
++ if (loop == 2)
++ return 0;
++ }
++ return 1;
++}
++
++static bool finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0;
++ bool status = false;
++FINETUNE_START:
++ for (cnt = 0; cnt < 16; cnt++) {
++ dllmin[cnt] = 0xff;
++ dllmax[cnt] = 0x0;
++ }
++ passcnt = 0;
++ for (dlli = 0; dlli < 76; dlli++) {
++ ast_moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
++ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
++ data = cbr_scan2(ast);
++ if (data != 0) {
++ mask = 0x00010001;
++ for (cnt = 0; cnt < 16; cnt++) {
++ if (data & mask) {
++ if (dllmin[cnt] > dlli) {
++ dllmin[cnt] = dlli;
++ }
++ if (dllmax[cnt] < dlli) {
++ dllmax[cnt] = dlli;
++ }
++ }
++ mask <<= 1;
++ }
++ passcnt++;
++ } else if (passcnt >= CBR_THRESHOLD2) {
++ break;
++ }
++ }
++ gold_sadj[0] = 0x0;
++ passcnt = 0;
++ for (cnt = 0; cnt < 16; cnt++) {
++ if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
++ gold_sadj[0] += dllmin[cnt];
++ passcnt++;
++ }
++ }
++ if (retry++ > 10)
++ goto FINETUNE_DONE;
++ if (passcnt != 16) {
++ goto FINETUNE_START;
++ }
++ status = true;
++FINETUNE_DONE:
++ gold_sadj[0] = gold_sadj[0] >> 4;
++ gold_sadj[1] = gold_sadj[0];
++
++ data = 0;
++ for (cnt = 0; cnt < 8; cnt++) {
++ data >>= 3;
++ if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
++ dlli = dllmin[cnt];
++ if (gold_sadj[0] >= dlli) {
++ dlli = ((gold_sadj[0] - dlli) * 19) >> 5;
++ if (dlli > 3) {
++ dlli = 3;
++ }
++ } else {
++ dlli = ((dlli - gold_sadj[0]) * 19) >> 5;
++ if (dlli > 4) {
++ dlli = 4;
++ }
++ dlli = (8 - dlli) & 0x7;
++ }
++ data |= dlli << 21;
++ }
++ }
++ ast_moutdwm(ast, 0x1E6E0080, data);
++
++ data = 0;
++ for (cnt = 8; cnt < 16; cnt++) {
++ data >>= 3;
++ if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
++ dlli = dllmin[cnt];
++ if (gold_sadj[1] >= dlli) {
++ dlli = ((gold_sadj[1] - dlli) * 19) >> 5;
++ if (dlli > 3) {
++ dlli = 3;
++ } else {
++ dlli = (dlli - 1) & 0x7;
++ }
++ } else {
++ dlli = ((dlli - gold_sadj[1]) * 19) >> 5;
++ dlli += 1;
++ if (dlli > 4) {
++ dlli = 4;
++ }
++ dlli = (8 - dlli) & 0x7;
++ }
++ data |= dlli << 21;
++ }
++ }
++ ast_moutdwm(ast, 0x1E6E0084, data);
++ return status;
++} /* finetuneDQI_L */
++
++static void finetuneDQSI(struct ast_private *ast)
++{
++ u32 dlli, dqsip, dqidly;
++ u32 reg_mcr18, reg_mcr0c, passcnt[2], diff;
++ u32 g_dqidly, g_dqsip, g_margin, g_side;
++ u16 pass[32][2][2];
++ char tag[2][76];
++
++ /* Disable DQI CBR */
++ reg_mcr0c = ast_mindwm(ast, 0x1E6E000C);
++ reg_mcr18 = ast_mindwm(ast, 0x1E6E0018);
++ reg_mcr18 &= 0x0000ffff;
++ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
++
++ for (dlli = 0; dlli < 76; dlli++) {
++ tag[0][dlli] = 0x0;
++ tag[1][dlli] = 0x0;
++ }
++ for (dqidly = 0; dqidly < 32; dqidly++) {
++ pass[dqidly][0][0] = 0xff;
++ pass[dqidly][0][1] = 0x0;
++ pass[dqidly][1][0] = 0xff;
++ pass[dqidly][1][1] = 0x0;
++ }
++ for (dqidly = 0; dqidly < 32; dqidly++) {
++ passcnt[0] = passcnt[1] = 0;
++ for (dqsip = 0; dqsip < 2; dqsip++) {
++ ast_moutdwm(ast, 0x1E6E000C, 0);
++ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
++ ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c);
++ for (dlli = 0; dlli < 76; dlli++) {
++ ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
++ ast_moutdwm(ast, 0x1E6E0070, 0);
++ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE0);
++ if (cbr_scan3(ast)) {
++ if (dlli == 0)
++ break;
++ passcnt[dqsip]++;
++ tag[dqsip][dlli] = 'P';
++ if (dlli < pass[dqidly][dqsip][0])
++ pass[dqidly][dqsip][0] = (u16) dlli;
++ if (dlli > pass[dqidly][dqsip][1])
++ pass[dqidly][dqsip][1] = (u16) dlli;
++ } else if (passcnt[dqsip] >= 5)
++ break;
++ else {
++ pass[dqidly][dqsip][0] = 0xff;
++ pass[dqidly][dqsip][1] = 0x0;
++ }
++ }
++ }
++ if (passcnt[0] == 0 && passcnt[1] == 0)
++ dqidly++;
++ }
++ /* Search margin */
++ g_dqidly = g_dqsip = g_margin = g_side = 0;
++
++ for (dqidly = 0; dqidly < 32; dqidly++) {
++ for (dqsip = 0; dqsip < 2; dqsip++) {
++ if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1])
++ continue;
++ diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0];
++ if ((diff+2) < g_margin)
++ continue;
++ passcnt[0] = passcnt[1] = 0;
++ for (dlli = pass[dqidly][dqsip][0]; dlli > 0 && tag[dqsip][dlli] != 0; dlli--, passcnt[0]++);
++ for (dlli = pass[dqidly][dqsip][1]; dlli < 76 && tag[dqsip][dlli] != 0; dlli++, passcnt[1]++);
++ if (passcnt[0] > passcnt[1])
++ passcnt[0] = passcnt[1];
++ passcnt[1] = 0;
++ if (passcnt[0] > g_side)
++ passcnt[1] = passcnt[0] - g_side;
++ if (diff > (g_margin+1) && (passcnt[1] > 0 || passcnt[0] > 8)) {
++ g_margin = diff;
++ g_dqidly = dqidly;
++ g_dqsip = dqsip;
++ g_side = passcnt[0];
++ } else if (passcnt[1] > 1 && g_side < 8) {
++ if (diff > g_margin)
++ g_margin = diff;
++ g_dqidly = dqidly;
++ g_dqsip = dqsip;
++ g_side = passcnt[0];
++ }
++ }
++ }
++ reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23);
++ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
++
++}
++static bool cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0;
++ bool status = false;
++
++ finetuneDQSI(ast);
++ if (finetuneDQI_L(ast, param) == false)
++ return status;
++
++CBR_START2:
++ dllmin[0] = dllmin[1] = 0xff;
++ dllmax[0] = dllmax[1] = 0x0;
++ passcnt = 0;
++ for (dlli = 0; dlli < 76; dlli++) {
++ ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
++ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
++ data = cbr_scan(ast);
++ if (data != 0) {
++ if (data & 0x1) {
++ if (dllmin[0] > dlli) {
++ dllmin[0] = dlli;
++ }
++ if (dllmax[0] < dlli) {
++ dllmax[0] = dlli;
++ }
++ }
++ if (data & 0x2) {
++ if (dllmin[1] > dlli) {
++ dllmin[1] = dlli;
++ }
++ if (dllmax[1] < dlli) {
++ dllmax[1] = dlli;
++ }
++ }
++ passcnt++;
++ } else if (passcnt >= CBR_THRESHOLD) {
++ break;
++ }
++ }
++ if (retry++ > 10)
++ goto CBR_DONE2;
++ if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) {
++ goto CBR_START2;
++ }
++ if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) {
++ goto CBR_START2;
++ }
++ status = true;
++CBR_DONE2:
++ dlli = (dllmin[1] + dllmax[1]) >> 1;
++ dlli <<= 8;
++ dlli += (dllmin[0] + dllmax[0]) >> 1;
++ ast_moutdwm(ast, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16));
++ return status;
++} /* CBRDLL2 */
++
++static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 trap, trap_AC2, trap_MRS;
++
++ ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
++
++ /* Ger trap info */
++ trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
++ trap_AC2 = 0x00020000 + (trap << 16);
++ trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
++ trap_MRS = 0x00000010 + (trap << 4);
++ trap_MRS |= ((trap & 0x2) << 18);
++
++ param->reg_MADJ = 0x00034C4C;
++ param->reg_SADJ = 0x00001800;
++ param->reg_DRV = 0x000000F0;
++ param->reg_PERIOD = param->dram_freq;
++ param->rodt = 0;
++
++ switch (param->dram_freq) {
++ case 336:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0190);
++ param->wodt = 0;
++ param->reg_AC1 = 0x22202725;
++ param->reg_AC2 = 0xAA007613 | trap_AC2;
++ param->reg_DQSIC = 0x000000BA;
++ param->reg_MRS = 0x04001400 | trap_MRS;
++ param->reg_EMRS = 0x00000000;
++ param->reg_IOZ = 0x00000023;
++ param->reg_DQIDLY = 0x00000074;
++ param->reg_FREQ = 0x00004DC0;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 3;
++ switch (param->dram_chipid) {
++ default:
++ case AST_DRAM_512Mx16:
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xAA007613 | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xAA00761C | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xAA007636 | trap_AC2;
++ break;
++ }
++ break;
++ default:
++ case 396:
++ ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
++ param->wodt = 1;
++ param->reg_AC1 = 0x33302825;
++ param->reg_AC2 = 0xCC009617 | trap_AC2;
++ param->reg_DQSIC = 0x000000E2;
++ param->reg_MRS = 0x04001600 | trap_MRS;
++ param->reg_EMRS = 0x00000000;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DRV = 0x000000FA;
++ param->reg_DQIDLY = 0x00000089;
++ param->reg_FREQ = 0x00005040;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 4;
++
++ switch (param->dram_chipid) {
++ default:
++ case AST_DRAM_512Mx16:
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xCC009617 | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xCC009622 | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xCC00963F | trap_AC2;
++ break;
++ }
++ break;
++
++ case 408:
++ ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
++ param->wodt = 1;
++ param->reg_AC1 = 0x33302825;
++ param->reg_AC2 = 0xCC009617 | trap_AC2;
++ param->reg_DQSIC = 0x000000E2;
++ param->reg_MRS = 0x04001600 | trap_MRS;
++ param->reg_EMRS = 0x00000000;
++ param->reg_IOZ = 0x00000023;
++ param->reg_DRV = 0x000000FA;
++ param->reg_DQIDLY = 0x00000089;
++ param->reg_FREQ = 0x000050C0;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 4;
++
++ switch (param->dram_chipid) {
++ default:
++ case AST_DRAM_512Mx16:
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xCC009617 | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xCC009622 | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xCC00963F | trap_AC2;
++ break;
++ }
++
++ break;
++ case 456:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0230);
++ param->wodt = 0;
++ param->reg_AC1 = 0x33302926;
++ param->reg_AC2 = 0xCD44961A;
++ param->reg_DQSIC = 0x000000FC;
++ param->reg_MRS = 0x00081830;
++ param->reg_EMRS = 0x00000000;
++ param->reg_IOZ = 0x00000045;
++ param->reg_DQIDLY = 0x00000097;
++ param->reg_FREQ = 0x000052C0;
++ param->madj_max = 88;
++ param->dll2_finetune_step = 4;
++ break;
++ case 504:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0270);
++ param->wodt = 1;
++ param->reg_AC1 = 0x33302926;
++ param->reg_AC2 = 0xDE44A61D;
++ param->reg_DQSIC = 0x00000117;
++ param->reg_MRS = 0x00081A30;
++ param->reg_EMRS = 0x00000000;
++ param->reg_IOZ = 0x070000BB;
++ param->reg_DQIDLY = 0x000000A0;
++ param->reg_FREQ = 0x000054C0;
++ param->madj_max = 79;
++ param->dll2_finetune_step = 4;
++ break;
++ case 528:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0290);
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x33302926;
++ param->reg_AC2 = 0xEF44B61E;
++ param->reg_DQSIC = 0x00000125;
++ param->reg_MRS = 0x00081A30;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x000000F5;
++ param->reg_IOZ = 0x00000023;
++ param->reg_DQIDLY = 0x00000088;
++ param->reg_FREQ = 0x000055C0;
++ param->madj_max = 76;
++ param->dll2_finetune_step = 3;
++ break;
++ case 576:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0140);
++ param->reg_MADJ = 0x00136868;
++ param->reg_SADJ = 0x00004534;
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x33302A37;
++ param->reg_AC2 = 0xEF56B61E;
++ param->reg_DQSIC = 0x0000013F;
++ param->reg_MRS = 0x00101A50;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x000000FA;
++ param->reg_IOZ = 0x00000023;
++ param->reg_DQIDLY = 0x00000078;
++ param->reg_FREQ = 0x000057C0;
++ param->madj_max = 136;
++ param->dll2_finetune_step = 3;
++ break;
++ case 600:
++ ast_moutdwm(ast, 0x1E6E2020, 0x02E1);
++ param->reg_MADJ = 0x00136868;
++ param->reg_SADJ = 0x00004534;
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x32302A37;
++ param->reg_AC2 = 0xDF56B61F;
++ param->reg_DQSIC = 0x0000014D;
++ param->reg_MRS = 0x00101A50;
++ param->reg_EMRS = 0x00000004;
++ param->reg_DRV = 0x000000F5;
++ param->reg_IOZ = 0x00000023;
++ param->reg_DQIDLY = 0x00000078;
++ param->reg_FREQ = 0x000058C0;
++ param->madj_max = 132;
++ param->dll2_finetune_step = 3;
++ break;
++ case 624:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0160);
++ param->reg_MADJ = 0x00136868;
++ param->reg_SADJ = 0x00004534;
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x32302A37;
++ param->reg_AC2 = 0xEF56B621;
++ param->reg_DQSIC = 0x0000015A;
++ param->reg_MRS = 0x02101A50;
++ param->reg_EMRS = 0x00000004;
++ param->reg_DRV = 0x000000F5;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x00000078;
++ param->reg_FREQ = 0x000059C0;
++ param->madj_max = 128;
++ param->dll2_finetune_step = 3;
++ break;
++ } /* switch freq */
++
++ switch (param->dram_chipid) {
++ case AST_DRAM_512Mx16:
++ param->dram_config = 0x130;
++ break;
++ default:
++ case AST_DRAM_1Gx16:
++ param->dram_config = 0x131;
++ break;
++ case AST_DRAM_2Gx16:
++ param->dram_config = 0x132;
++ break;
++ case AST_DRAM_4Gx16:
++ param->dram_config = 0x133;
++ break;
++ } /* switch size */
++
++ switch (param->vram_size) {
++ default:
++ case AST_VIDMEM_SIZE_8M:
++ param->dram_config |= 0x00;
++ break;
++ case AST_VIDMEM_SIZE_16M:
++ param->dram_config |= 0x04;
++ break;
++ case AST_VIDMEM_SIZE_32M:
++ param->dram_config |= 0x08;
++ break;
++ case AST_VIDMEM_SIZE_64M:
++ param->dram_config |= 0x0c;
++ break;
++ }
++
++}
++
++static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 data, data2, retry = 0;
++
++ddr3_init_start:
++ ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
++ ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
++ ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0034, 0x00000000);
++ udelay(10);
++ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
++ ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
++ udelay(10);
++ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
++ udelay(10);
++
++ ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
++ ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
++ ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
++ ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
++ ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
++ ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
++ ast_moutdwm(ast, 0x1E6E0018, 0x4000A170);
++ ast_moutdwm(ast, 0x1E6E0018, 0x00002370);
++ ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0040, 0xFF444444);
++ ast_moutdwm(ast, 0x1E6E0044, 0x22222222);
++ ast_moutdwm(ast, 0x1E6E0048, 0x22222222);
++ ast_moutdwm(ast, 0x1E6E004C, 0x00000002);
++ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0054, 0);
++ ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
++ ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
++ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
++ /* Wait MCLK2X lock to MCLK */
++ do {
++ data = ast_mindwm(ast, 0x1E6E001C);
++ } while (!(data & 0x08000000));
++ data = ast_mindwm(ast, 0x1E6E001C);
++ data = (data >> 8) & 0xff;
++ while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
++ data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
++ if ((data2 & 0xff) > param->madj_max) {
++ break;
++ }
++ ast_moutdwm(ast, 0x1E6E0064, data2);
++ if (data2 & 0x00100000) {
++ data2 = ((data2 & 0xff) >> 3) + 3;
++ } else {
++ data2 = ((data2 & 0xff) >> 2) + 5;
++ }
++ data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
++ data2 += data & 0xff;
++ data = data | (data2 << 8);
++ ast_moutdwm(ast, 0x1E6E0068, data);
++ udelay(10);
++ ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
++ udelay(10);
++ data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++ data = data | 0x200;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++ do {
++ data = ast_mindwm(ast, 0x1E6E001C);
++ } while (!(data & 0x08000000));
++
++ data = ast_mindwm(ast, 0x1E6E001C);
++ data = (data >> 8) & 0xff;
++ }
++ ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff);
++ data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++
++ ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
++ ast_moutdwm(ast, 0x1E6E000C, 0x00000040);
++ udelay(50);
++ /* Mode Register Setting */
++ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
++ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
++ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
++ ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
++
++ ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
++ data = 0;
++ if (param->wodt) {
++ data = 0x300;
++ }
++ if (param->rodt) {
++ data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
++ }
++ ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
++
++ /* Calibrate the DQSI delay */
++ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
++ goto ddr3_init_start;
++
++ ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
++ /* ECC Memory Initialization */
++#ifdef ECC
++ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0070, 0x221);
++ do {
++ data = ast_mindwm(ast, 0x1E6E0070);
++ } while (!(data & 0x00001000));
++ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
++#endif
++
++
++}
++
++static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 trap, trap_AC2, trap_MRS;
++
++ ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
++
++ /* Ger trap info */
++ trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
++ trap_AC2 = (trap << 20) | (trap << 16);
++ trap_AC2 += 0x00110000;
++ trap_MRS = 0x00000040 | (trap << 4);
++
++
++ param->reg_MADJ = 0x00034C4C;
++ param->reg_SADJ = 0x00001800;
++ param->reg_DRV = 0x000000F0;
++ param->reg_PERIOD = param->dram_freq;
++ param->rodt = 0;
++
++ switch (param->dram_freq) {
++ case 264:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0130);
++ param->wodt = 0;
++ param->reg_AC1 = 0x11101513;
++ param->reg_AC2 = 0x78117011;
++ param->reg_DQSIC = 0x00000092;
++ param->reg_MRS = 0x00000842;
++ param->reg_EMRS = 0x00000000;
++ param->reg_DRV = 0x000000F0;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x0000005A;
++ param->reg_FREQ = 0x00004AC0;
++ param->madj_max = 138;
++ param->dll2_finetune_step = 3;
++ break;
++ case 336:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0190);
++ param->wodt = 1;
++ param->reg_AC1 = 0x22202613;
++ param->reg_AC2 = 0xAA009016 | trap_AC2;
++ param->reg_DQSIC = 0x000000BA;
++ param->reg_MRS = 0x00000A02 | trap_MRS;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x000000FA;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x00000074;
++ param->reg_FREQ = 0x00004DC0;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 3;
++ switch (param->dram_chipid) {
++ default:
++ case AST_DRAM_512Mx16:
++ param->reg_AC2 = 0xAA009012 | trap_AC2;
++ break;
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xAA009016 | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xAA009023 | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xAA00903B | trap_AC2;
++ break;
++ }
++ break;
++ default:
++ case 396:
++ ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
++ param->wodt = 1;
++ param->rodt = 0;
++ param->reg_AC1 = 0x33302714;
++ param->reg_AC2 = 0xCC00B01B | trap_AC2;
++ param->reg_DQSIC = 0x000000E2;
++ param->reg_MRS = 0x00000C02 | trap_MRS;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x000000FA;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x00000089;
++ param->reg_FREQ = 0x00005040;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 4;
++
++ switch (param->dram_chipid) {
++ case AST_DRAM_512Mx16:
++ param->reg_AC2 = 0xCC00B016 | trap_AC2;
++ break;
++ default:
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xCC00B01B | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xCC00B02B | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xCC00B03F | trap_AC2;
++ break;
++ }
++
++ break;
++
++ case 408:
++ ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
++ param->wodt = 1;
++ param->rodt = 0;
++ param->reg_AC1 = 0x33302714;
++ param->reg_AC2 = 0xCC00B01B | trap_AC2;
++ param->reg_DQSIC = 0x000000E2;
++ param->reg_MRS = 0x00000C02 | trap_MRS;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x000000FA;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x00000089;
++ param->reg_FREQ = 0x000050C0;
++ param->madj_max = 96;
++ param->dll2_finetune_step = 4;
++
++ switch (param->dram_chipid) {
++ case AST_DRAM_512Mx16:
++ param->reg_AC2 = 0xCC00B016 | trap_AC2;
++ break;
++ default:
++ case AST_DRAM_1Gx16:
++ param->reg_AC2 = 0xCC00B01B | trap_AC2;
++ break;
++ case AST_DRAM_2Gx16:
++ param->reg_AC2 = 0xCC00B02B | trap_AC2;
++ break;
++ case AST_DRAM_4Gx16:
++ param->reg_AC2 = 0xCC00B03F | trap_AC2;
++ break;
++ }
++
++ break;
++ case 456:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0230);
++ param->wodt = 0;
++ param->reg_AC1 = 0x33302815;
++ param->reg_AC2 = 0xCD44B01E;
++ param->reg_DQSIC = 0x000000FC;
++ param->reg_MRS = 0x00000E72;
++ param->reg_EMRS = 0x00000000;
++ param->reg_DRV = 0x00000000;
++ param->reg_IOZ = 0x00000034;
++ param->reg_DQIDLY = 0x00000097;
++ param->reg_FREQ = 0x000052C0;
++ param->madj_max = 88;
++ param->dll2_finetune_step = 3;
++ break;
++ case 504:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0261);
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x33302815;
++ param->reg_AC2 = 0xDE44C022;
++ param->reg_DQSIC = 0x00000117;
++ param->reg_MRS = 0x00000E72;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x0000000A;
++ param->reg_IOZ = 0x00000045;
++ param->reg_DQIDLY = 0x000000A0;
++ param->reg_FREQ = 0x000054C0;
++ param->madj_max = 79;
++ param->dll2_finetune_step = 3;
++ break;
++ case 528:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0120);
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x33302815;
++ param->reg_AC2 = 0xEF44D024;
++ param->reg_DQSIC = 0x00000125;
++ param->reg_MRS = 0x00000E72;
++ param->reg_EMRS = 0x00000004;
++ param->reg_DRV = 0x000000F9;
++ param->reg_IOZ = 0x00000045;
++ param->reg_DQIDLY = 0x000000A7;
++ param->reg_FREQ = 0x000055C0;
++ param->madj_max = 76;
++ param->dll2_finetune_step = 3;
++ break;
++ case 552:
++ ast_moutdwm(ast, 0x1E6E2020, 0x02A1);
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x43402915;
++ param->reg_AC2 = 0xFF44E025;
++ param->reg_DQSIC = 0x00000132;
++ param->reg_MRS = 0x00000E72;
++ param->reg_EMRS = 0x00000040;
++ param->reg_DRV = 0x0000000A;
++ param->reg_IOZ = 0x00000045;
++ param->reg_DQIDLY = 0x000000AD;
++ param->reg_FREQ = 0x000056C0;
++ param->madj_max = 76;
++ param->dll2_finetune_step = 3;
++ break;
++ case 576:
++ ast_moutdwm(ast, 0x1E6E2020, 0x0140);
++ param->wodt = 1;
++ param->rodt = 1;
++ param->reg_AC1 = 0x43402915;
++ param->reg_AC2 = 0xFF44E027;
++ param->reg_DQSIC = 0x0000013F;
++ param->reg_MRS = 0x00000E72;
++ param->reg_EMRS = 0x00000004;
++ param->reg_DRV = 0x000000F5;
++ param->reg_IOZ = 0x00000045;
++ param->reg_DQIDLY = 0x000000B3;
++ param->reg_FREQ = 0x000057C0;
++ param->madj_max = 76;
++ param->dll2_finetune_step = 3;
++ break;
++ }
++
++ switch (param->dram_chipid) {
++ case AST_DRAM_512Mx16:
++ param->dram_config = 0x100;
++ break;
++ default:
++ case AST_DRAM_1Gx16:
++ param->dram_config = 0x121;
++ break;
++ case AST_DRAM_2Gx16:
++ param->dram_config = 0x122;
++ break;
++ case AST_DRAM_4Gx16:
++ param->dram_config = 0x123;
++ break;
++ } /* switch size */
++
++ switch (param->vram_size) {
++ default:
++ case AST_VIDMEM_SIZE_8M:
++ param->dram_config |= 0x00;
++ break;
++ case AST_VIDMEM_SIZE_16M:
++ param->dram_config |= 0x04;
++ break;
++ case AST_VIDMEM_SIZE_32M:
++ param->dram_config |= 0x08;
++ break;
++ case AST_VIDMEM_SIZE_64M:
++ param->dram_config |= 0x0c;
++ break;
++ }
++}
++
++static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
++{
++ u32 data, data2, retry = 0;
++
++ddr2_init_start:
++ ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
++ ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
++ ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
++ ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
++ udelay(10);
++ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
++ udelay(10);
++
++ ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
++ ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
++ ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
++ ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
++ ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
++ ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
++ ast_moutdwm(ast, 0x1E6E0018, 0x4000A130);
++ ast_moutdwm(ast, 0x1E6E0018, 0x00002330);
++ ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0040, 0xFF808000);
++ ast_moutdwm(ast, 0x1E6E0044, 0x88848466);
++ ast_moutdwm(ast, 0x1E6E0048, 0x44440008);
++ ast_moutdwm(ast, 0x1E6E004C, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0054, 0);
++ ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
++ ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
++ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
++
++ /* Wait MCLK2X lock to MCLK */
++ do {
++ data = ast_mindwm(ast, 0x1E6E001C);
++ } while (!(data & 0x08000000));
++ data = ast_mindwm(ast, 0x1E6E001C);
++ data = (data >> 8) & 0xff;
++ while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
++ data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
++ if ((data2 & 0xff) > param->madj_max) {
++ break;
++ }
++ ast_moutdwm(ast, 0x1E6E0064, data2);
++ if (data2 & 0x00100000) {
++ data2 = ((data2 & 0xff) >> 3) + 3;
++ } else {
++ data2 = ((data2 & 0xff) >> 2) + 5;
++ }
++ data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
++ data2 += data & 0xff;
++ data = data | (data2 << 8);
++ ast_moutdwm(ast, 0x1E6E0068, data);
++ udelay(10);
++ ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
++ udelay(10);
++ data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++ data = data | 0x200;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++ do {
++ data = ast_mindwm(ast, 0x1E6E001C);
++ } while (!(data & 0x08000000));
++
++ data = ast_mindwm(ast, 0x1E6E001C);
++ data = (data >> 8) & 0xff;
++ }
++ ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff);
++ data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
++ ast_moutdwm(ast, 0x1E6E0018, data);
++
++ ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
++ ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
++ udelay(50);
++ /* Mode Register Setting */
++ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
++ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
++
++ ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
++ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
++ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
++ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
++ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
++
++ ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
++ data = 0;
++ if (param->wodt) {
++ data = 0x500;
++ }
++ if (param->rodt) {
++ data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
++ }
++ ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
++ ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
++
++ /* Calibrate the DQSI delay */
++ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
++ goto ddr2_init_start;
++
++ /* ECC Memory Initialization */
++#ifdef ECC
++ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0070, 0x221);
++ do {
++ data = ast_mindwm(ast, 0x1E6E0070);
++ } while (!(data & 0x00001000));
++ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
++ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
++#endif
++
++}
++
++static void ast_init_dram_2300(struct drm_device *dev)
++{
++ struct ast_private *ast = dev->dev_private;
++ struct ast2300_dram_param param;
++ u32 temp;
++ u8 reg;
++
++ reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ if ((reg & 0x80) == 0) {/* vga only */
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ ast_write32(ast, 0x12000, 0x1688a8a8);
++ do {
++ ;
++ } while (ast_read32(ast, 0x12000) != 0x1);
++
++ ast_write32(ast, 0x10000, 0xfc600309);
++ do {
++ ;
++ } while (ast_read32(ast, 0x10000) != 0x1);
++
++ /* Slow down CPU/AHB CLK in VGA only mode */
++ temp = ast_read32(ast, 0x12008);
++ temp |= 0x73;
++ ast_write32(ast, 0x12008, temp);
++
++ param.dram_type = AST_DDR3;
++ if (temp & 0x01000000)
++ param.dram_type = AST_DDR2;
++ param.dram_chipid = ast->dram_type;
++ param.dram_freq = ast->mclk;
++ param.vram_size = ast->vram_size;
++
++ if (param.dram_type == AST_DDR3) {
++ get_ddr3_info(ast, &param);
++ ddr3_init(ast, &param);
++ } else {
++ get_ddr2_info(ast, &param);
++ ddr2_init(ast, &param);
++ }
++
++ temp = ast_mindwm(ast, 0x1e6e2040);
++ ast_moutdwm(ast, 0x1e6e2040, temp | 0x40);
++ }
++
++ /* wait ready */
++ do {
++ reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ } while ((reg & 0x40) == 0);
++}
++
+diff --git a/src/drivers/aspeed/common/ast_tables.h b/src/drivers/aspeed/common/ast_tables.h
+new file mode 100644
+index 0000000..3608d5a
+--- /dev/null
++++ b/src/drivers/aspeed/common/ast_tables.h
+@@ -0,0 +1,305 @@
++/*
++ * Copyright (c) 2005 ASPEED Technology Inc.
++ *
++ * Permission to use, copy, modify, distribute, and sell this software and its
++ * documentation for any purpose is hereby granted without fee, provided that
++ * the above copyright notice appear in all copies and that both that
++ * copyright notice and this permission notice appear in supporting
++ * documentation, and that the name of the authors not be used in
++ * advertising or publicity pertaining to distribution of the software without
++ * specific, written prior permission. The authors makes no representations
++ * about the suitability of this software for any purpose. It is provided
++ * "as is" without express or implied warranty.
++ *
++ * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
++ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
++ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++/* Ported from xf86-video-ast driver */
++
++#ifndef AST_TABLES_H
++#define AST_TABLES_H
++
++/* Std. Table Index Definition */
++#define TextModeIndex 0
++#define EGAModeIndex 1
++#define VGAModeIndex 2
++#define HiCModeIndex 3
++#define TrueCModeIndex 4
++
++#define Charx8Dot 0x00000001
++#define HalfDCLK 0x00000002
++#define DoubleScanMode 0x00000004
++#define LineCompareOff 0x00000008
++#define HBorder 0x00000020
++#define VBorder 0x00000010
++#define WideScreenMode 0x00000100
++#define NewModeInfo 0x00000200
++#define NHSync 0x00000400
++#define PHSync 0x00000800
++#define NVSync 0x00001000
++#define PVSync 0x00002000
++#define SyncPP (PVSync | PHSync)
++#define SyncPN (PVSync | NHSync)
++#define SyncNP (NVSync | PHSync)
++#define SyncNN (NVSync | NHSync)
++
++/* DCLK Index */
++#define VCLK25_175 0x00
++#define VCLK28_322 0x01
++#define VCLK31_5 0x02
++#define VCLK36 0x03
++#define VCLK40 0x04
++#define VCLK49_5 0x05
++#define VCLK50 0x06
++#define VCLK56_25 0x07
++#define VCLK65 0x08
++#define VCLK75 0x09
++#define VCLK78_75 0x0A
++#define VCLK94_5 0x0B
++#define VCLK108 0x0C
++#define VCLK135 0x0D
++#define VCLK157_5 0x0E
++#define VCLK162 0x0F
++/* #define VCLK193_25 0x10 */
++#define VCLK154 0x10
++#define VCLK83_5 0x11
++#define VCLK106_5 0x12
++#define VCLK146_25 0x13
++#define VCLK148_5 0x14
++#define VCLK71 0x15
++#define VCLK88_75 0x16
++#define VCLK119 0x17
++#define VCLK85_5 0x18
++#define VCLK97_75 0x19
++#define VCLK118_25 0x1A
++
++static struct ast_vbios_dclk_info dclk_table[] = {
++ {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
++ {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
++ {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
++ {0x76, 0x63, 0x01}, /* 03: VCLK36 */
++ {0xEE, 0x67, 0x01}, /* 04: VCLK40 */
++ {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
++ {0xC6, 0x64, 0x01}, /* 06: VCLK50 */
++ {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
++ {0x80, 0x64, 0x00}, /* 08: VCLK65 */
++ {0x7B, 0x63, 0x00}, /* 09: VCLK75 */
++ {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */
++ {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */
++ {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */
++ {0x85, 0x24, 0x00}, /* 0D: VCLK135 */
++ {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */
++ {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */
++ {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
++ {0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */
++ {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
++ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
++ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
++ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
++ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
++ {0x77, 0x58, 0x80}, /* 17: VCLK119 */
++ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
++ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
++ {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */
++};
++
++static struct ast_vbios_stdtable vbios_stdtable[] = {
++ /* MD_2_3_400 */
++ {
++ 0x67,
++ {0x00,0x03,0x00,0x02},
++ {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f,
++ 0x00,0x4f,0x0d,0x0e,0x00,0x00,0x00,0x00,
++ 0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3,
++ 0xff},
++ {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
++ 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
++ 0x0c,0x00,0x0f,0x08},
++ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00,
++ 0xff}
++ },
++ /* Mode12/ExtEGATable */
++ {
++ 0xe3,
++ {0x01,0x0f,0x00,0x06},
++ {0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e,
++ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0xe9,0x8b,0xdf,0x28,0x00,0xe7,0x04,0xe3,
++ 0xff},
++ {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
++ 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
++ 0x01,0x00,0x0f,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
++ 0xff}
++ },
++ /* ExtVGATable */
++ {
++ 0x2f,
++ {0x01,0x0f,0x00,0x0e},
++ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
++ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
++ 0xff},
++ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
++ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
++ 0x01,0x00,0x00,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
++ 0xff}
++ },
++ /* ExtHiCTable */
++ {
++ 0x2f,
++ {0x01,0x0f,0x00,0x0e},
++ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
++ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
++ 0xff},
++ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
++ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
++ 0x01,0x00,0x00,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
++ 0xff}
++ },
++ /* ExtTrueCTable */
++ {
++ 0x2f,
++ {0x01,0x0f,0x00,0x0e},
++ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
++ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
++ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
++ 0xff},
++ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
++ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
++ 0x01,0x00,0x00,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
++ 0xff}
++ },
++};
++
++static struct ast_vbios_enhtable res_640x480[] = {
++ { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60Hz */
++ (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2E },
++ { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72Hz */
++ (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2E },
++ { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75Hz */
++ (SyncNN | Charx8Dot) , 75, 3, 0x2E },
++ { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */
++ (SyncNN | Charx8Dot) , 85, 4, 0x2E },
++ { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */
++ (SyncNN | Charx8Dot) , 0xFF, 4, 0x2E },
++};
++
++static struct ast_vbios_enhtable res_800x600[] = {
++ {1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */
++ (SyncPP | Charx8Dot), 56, 1, 0x30 },
++ {1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60Hz */
++ (SyncPP | Charx8Dot), 60, 2, 0x30 },
++ {1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72Hz */
++ (SyncPP | Charx8Dot), 72, 3, 0x30 },
++ {1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75Hz */
++ (SyncPP | Charx8Dot), 75, 4, 0x30 },
++ {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85Hz */
++ (SyncPP | Charx8Dot), 84, 5, 0x30 },
++ {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* end */
++ (SyncPP | Charx8Dot), 0xFF, 5, 0x30 },
++};
++
++
++static struct ast_vbios_enhtable res_1024x768[] = {
++ {1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60Hz */
++ (SyncNN | Charx8Dot), 60, 1, 0x31 },
++ {1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70Hz */
++ (SyncNN | Charx8Dot), 70, 2, 0x31 },
++ {1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75Hz */
++ (SyncPP | Charx8Dot), 75, 3, 0x31 },
++ {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85Hz */
++ (SyncPP | Charx8Dot), 84, 4, 0x31 },
++ {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* end */
++ (SyncPP | Charx8Dot), 0xFF, 4, 0x31 },
++};
++
++static struct ast_vbios_enhtable res_1280x1024[] = {
++ {1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60Hz */
++ (SyncPP | Charx8Dot), 60, 1, 0x32 },
++ {1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75Hz */
++ (SyncPP | Charx8Dot), 75, 2, 0x32 },
++ {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85Hz */
++ (SyncPP | Charx8Dot), 85, 3, 0x32 },
++ {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* end */
++ (SyncPP | Charx8Dot), 0xFF, 3, 0x32 },
++};
++
++static struct ast_vbios_enhtable res_1600x1200[] = {
++ {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60Hz */
++ (SyncPP | Charx8Dot), 60, 1, 0x33 },
++ {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* end */
++ (SyncPP | Charx8Dot), 0xFF, 1, 0x33 },
++};
++
++/* 16:9 */
++static struct ast_vbios_enhtable res_1360x768[] = {
++ {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */
++ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 },
++ {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* end */
++ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x39 },
++};
++
++static struct ast_vbios_enhtable res_1600x900[] = {
++ {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A },
++ {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3A },
++ {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x3A },
++};
++
++static struct ast_vbios_enhtable res_1920x1080[] = {
++ {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x38 },
++ {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x38 },
++};
++
++
++/* 16:10 */
++static struct ast_vbios_enhtable res_1280x800[] = {
++ {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 },
++ {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 },
++ {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x35 },
++
++};
++
++static struct ast_vbios_enhtable res_1440x900[] = {
++ {1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
++ {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 },
++ {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x36 },
++};
++
++static struct ast_vbios_enhtable res_1680x1050[] = {
++ {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
++ {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 },
++ {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
++ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x37 },
++};
++
++static struct ast_vbios_enhtable res_1920x1200[] = {
++ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 },
++ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */
++ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 },
++};
++
++#endif
+diff --git a/src/include/device/pci_ids.h b/src/include/device/pci_ids.h
+index dcb8a42..fcaf4aa 100644
+--- a/src/include/device/pci_ids.h
++++ b/src/include/device/pci_ids.h
+@@ -1991,6 +1991,9 @@
+ #define PCI_DEVICE_ID_XGI_20 0x0020
+ #define PCI_DEVICE_ID_XGI_40 0x0040
+
++#define PCI_VENDOR_ID_ASPEED 0x1a03
++#define PCI_DEVICE_ID_ASPEED_AST2050_VGA 0x2000
++
+ #define PCI_VENDOR_ID_SYMPHONY 0x1c1c
+ #define PCI_DEVICE_ID_SYMPHONY_101 0x0001
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0008-southbridge-amd-sb700-Fix-boot-hang-on-ASUS-KGPE-D16.patch b/resources/libreboot/patch/kgpe-d16/0008-southbridge-amd-sb700-Fix-boot-hang-on-ASUS-KGPE-D16.patch
new file mode 100644
index 00000000..6792c6de
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0008-southbridge-amd-sb700-Fix-boot-hang-on-ASUS-KGPE-D16.patch
@@ -0,0 +1,641 @@
+From bdcf5ce540eb621d31655a172eb43ddf86d4cea0 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:46:15 -0500
+Subject: [PATCH 008/146] southbridge/amd/sb700: Fix boot hang on ASUS
+ KGPE-D16
+
+---
+ src/southbridge/amd/sb700/Kconfig | 4 +
+ src/southbridge/amd/sb700/acpi/ide.asl | 234 +++++++++++++++++++++++++++++++
+ src/southbridge/amd/sb700/acpi/sata.asl | 133 ++++++++++++++++++
+ src/southbridge/amd/sb700/bootblock.c | 46 +++++-
+ src/southbridge/amd/sb700/early_setup.c | 18 +++
+ src/southbridge/amd/sb700/lpc.c | 3 +
+ src/southbridge/amd/sb700/sm.c | 21 +--
+ src/southbridge/amd/sb700/smbus.h | 5 +-
+ 8 files changed, 447 insertions(+), 17 deletions(-)
+ create mode 100644 src/southbridge/amd/sb700/acpi/ide.asl
+ create mode 100644 src/southbridge/amd/sb700/acpi/sata.asl
+
+diff --git a/src/southbridge/amd/sb700/Kconfig b/src/southbridge/amd/sb700/Kconfig
+index 42ca2bb..064f32e 100644
+--- a/src/southbridge/amd/sb700/Kconfig
++++ b/src/southbridge/amd/sb700/Kconfig
+@@ -41,6 +41,10 @@ config SOUTHBRIDGE_AMD_SB700_SKIP_ISA_DMA_INIT
+ bool
+ default n
+
++config SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA
++ bool
++ default n
++
+ config EHCI_BAR
+ hex
+ default 0xfef00000
+diff --git a/src/southbridge/amd/sb700/acpi/ide.asl b/src/southbridge/amd/sb700/acpi/ide.asl
+new file mode 100644
+index 0000000..fa89d18
+--- /dev/null
++++ b/src/southbridge/amd/sb700/acpi/ide.asl
+@@ -0,0 +1,234 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/* Some timing tables */
++Name(UDTT, Package(){ /* Udma timing table */
++ 120, 90, 60, 45, 30, 20, 15, 0 /* UDMA modes 0 -> 6 */
++})
++
++Name(MDTT, Package(){ /* MWDma timing table */
++ 480, 150, 120, 0 /* Legacy DMA modes 0 -> 2 */
++})
++
++Name(POTT, Package(){ /* Pio timing table */
++ 600, 390, 270, 180, 120, 0 /* PIO modes 0 -> 4 */
++})
++
++/* Some timing register value tables */
++Name(MDRT, Package(){ /* MWDma timing register table */
++ 0x77, 0x21, 0x20, 0xFF /* Legacy DMA modes 0 -> 2 */
++})
++
++Name(PORT, Package(){
++ 0x99, 0x47, 0x34, 0x22, 0x20, 0x99 /* PIO modes 0 -> 4 */
++})
++
++OperationRegion(ICRG, PCI_Config, 0x40, 0x20) /* ide control registers */
++ Field(ICRG, AnyAcc, NoLock, Preserve)
++{
++ PPTS, 8, /* Primary PIO Slave Timing */
++ PPTM, 8, /* Primary PIO Master Timing */
++ OFFSET(0x04), PMTS, 8, /* Primary MWDMA Slave Timing */
++ PMTM, 8, /* Primary MWDMA Master Timing */
++ OFFSET(0x08), PPCR, 8, /* Primary PIO Control */
++ OFFSET(0x0A), PPMM, 4, /* Primary PIO master Mode */
++ PPSM, 4, /* Primary PIO slave Mode */
++ OFFSET(0x14), PDCR, 2, /* Primary UDMA Control */
++ OFFSET(0x16), PDMM, 4, /* Primary UltraDMA Mode */
++ PDSM, 4, /* Primary UltraDMA Mode */
++}
++
++Method(GTTM, 1) /* get total time*/
++{
++ Store(And(Arg0, 0x0F), Local0) /* Recovery Width */
++ Increment(Local0)
++ Store(ShiftRight(Arg0, 4), Local1) /* Command Width */
++ Increment(Local1)
++ Return(Multiply(30, Add(Local0, Local1)))
++}
++
++Device(PRID)
++{
++ Name (_ADR, Zero)
++ Method(_GTM, 0, Serialized)
++ {
++ NAME(OTBF, Buffer(20) { /* out buffer */
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
++ })
++
++ CreateDwordField(OTBF, 0, PSD0) /* PIO spd0 */
++ CreateDwordField(OTBF, 4, DSD0) /* DMA spd0 */
++ CreateDwordField(OTBF, 8, PSD1) /* PIO spd1 */
++ CreateDwordField(OTBF, 12, DSD1) /* DMA spd1 */
++ CreateDwordField(OTBF, 16, BFFG) /* buffer flags */
++
++ /* Just return if the channel is disabled */
++ If(And(PPCR, 0x01)) { /* primary PIO control */
++ Return(OTBF)
++ }
++
++ /* Always tell them independent timing available and IOChannelReady used on both drives */
++ Or(BFFG, 0x1A, BFFG)
++
++ Store(GTTM(PPTM), PSD0) /* save total time of primary PIO master timming to PIO spd0 */
++ Store(GTTM(PPTS), PSD1) /* save total time of primary PIO slave Timing to PIO spd1 */
++
++ If(And(PDCR, 0x01)) { /* It's under UDMA mode */
++ Or(BFFG, 0x01, BFFG)
++ Store(DerefOf(Index(UDTT, PDMM)), DSD0)
++ }
++ Else {
++ Store(GTTM(PMTM), DSD0) /* Primary MWDMA Master Timing, DmaSpd0 */
++ }
++
++ If(And(PDCR, 0x02)) { /* It's under UDMA mode */
++ Or(BFFG, 0x04, BFFG)
++ Store(DerefOf(Index(UDTT, PDSM)), DSD1)
++ }
++ Else {
++ Store(GTTM(PMTS), DSD1) /* Primary MWDMA Slave Timing, DmaSpd0 */
++ }
++
++ Return(OTBF) /* out buffer */
++ } /* End Method(_GTM) */
++
++ Method(_STM, 3, Serialized)
++ {
++ NAME(INBF, Buffer(20) { /* in buffer */
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
++ })
++
++ CreateDwordField(INBF, 0, PSD0) /* PIO spd0 */
++ CreateDwordField(INBF, 4, DSD0) /* PIO spd0 */
++ CreateDwordField(INBF, 8, PSD1) /* PIO spd1 */
++ CreateDwordField(INBF, 12, DSD1) /* DMA spd1 */
++ CreateDwordField(INBF, 16, BFFG) /*buffer flag */
++
++ Store(Match(POTT, MLE, PSD0, MTR, 0, 0), Local0)
++ Divide(Local0, 5, PPMM,) /* Primary PIO master Mode */
++ Store(Match(POTT, MLE, PSD1, MTR, 0, 0), Local1)
++ Divide(Local1, 5, PPSM,) /* Primary PIO slave Mode */
++
++ Store(DerefOf(Index(PORT, Local0)), PPTM) /* Primary PIO Master Timing */
++ Store(DerefOf(Index(PORT, Local1)), PPTS) /* Primary PIO Slave Timing */
++
++ If(And(BFFG, 0x01)) { /* Drive 0 is under UDMA mode */
++ Store(Match(UDTT, MLE, DSD0, MTR, 0, 0), Local0)
++ Divide(Local0, 7, PDMM,)
++ Or(PDCR, 0x01, PDCR)
++ }
++ Else {
++ If(LNotEqual(DSD0, 0xFFFFFFFF)) {
++ Store(Match(MDTT, MLE, DSD0, MTR, 0, 0), Local0)
++ Store(DerefOf(Index(MDRT, Local0)), PMTM)
++ }
++ }
++
++ If(And(BFFG, 0x04)) { /* Drive 1 is under UDMA mode */
++ Store(Match(UDTT, MLE, DSD1, MTR, 0, 0), Local0)
++ Divide(Local0, 7, PDSM,)
++ Or(PDCR, 0x02, PDCR)
++ }
++ Else {
++ If(LNotEqual(DSD1, 0xFFFFFFFF)) {
++ Store(Match(MDTT, MLE, DSD1, MTR, 0, 0), Local0)
++ Store(DerefOf(Index(MDRT, Local0)), PMTS)
++ }
++ }
++ /* Return(INBF) */
++ } /*End Method(_STM) */
++ Device(MST)
++ {
++ Name(_ADR, 0)
++ Method(_GTF, 0, Serialized) {
++ Name(CMBF, Buffer(21) {
++ 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
++ 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5
++ })
++ CreateByteField(CMBF, 1, POMD)
++ CreateByteField(CMBF, 8, DMMD)
++ CreateByteField(CMBF, 5, CMDA)
++ CreateByteField(CMBF, 12, CMDB)
++ CreateByteField(CMBF, 19, CMDC)
++
++ Store(0xA0, CMDA)
++ Store(0xA0, CMDB)
++ Store(0xA0, CMDC)
++
++ Or(PPMM, 0x08, POMD)
++
++ If(And(PDCR, 0x01)) {
++ Or(PDMM, 0x40, DMMD)
++ }
++ Else {
++ Store(Match
++ (MDTT, MLE, GTTM(PMTM),
++ MTR, 0, 0), Local0)
++ If(LLess(Local0, 3)) {
++ Or(0x20, Local0, DMMD)
++ }
++ }
++ Return(CMBF)
++ }
++ } /* End Device(MST) */
++
++ Device(SLAV)
++ {
++ Name(_ADR, 1)
++ Method(_GTF, 0, Serialized) {
++ Name(CMBF, Buffer(21) {
++ 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
++ 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5
++ })
++ CreateByteField(CMBF, 1, POMD)
++ CreateByteField(CMBF, 8, DMMD)
++ CreateByteField(CMBF, 5, CMDA)
++ CreateByteField(CMBF, 12, CMDB)
++ CreateByteField(CMBF, 19, CMDC)
++
++ Store(0xB0, CMDA)
++ Store(0xB0, CMDB)
++ Store(0xB0, CMDC)
++
++ Or(PPSM, 0x08, POMD)
++
++ If(And(PDCR, 0x02)) {
++ Or(PDSM, 0x40, DMMD)
++ }
++ Else {
++ Store(Match
++ (MDTT, MLE, GTTM(PMTS),
++ MTR, 0, 0), Local0)
++ If(LLess(Local0, 3)) {
++ Or(0x20, Local0, DMMD)
++ }
++ }
++ Return(CMBF)
++ }
++ } /* End Device(SLAV) */
++}
+diff --git a/src/southbridge/amd/sb700/acpi/sata.asl b/src/southbridge/amd/sb700/acpi/sata.asl
+new file mode 100644
+index 0000000..46a82b7
+--- /dev/null
++++ b/src/southbridge/amd/sb700/acpi/sata.asl
+@@ -0,0 +1,133 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++Name(STTM, Buffer(20) {
++ 0x78, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
++ 0x78, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
++ 0x1f, 0x00, 0x00, 0x00
++})
++
++/* Start by clearing the PhyRdyChg bits */
++Method(_INI) {
++ \_GPE._L1F()
++}
++
++Device(PMRY)
++{
++ Name(_ADR, 0)
++ Method(_GTM, 0x0, NotSerialized) {
++ Return(STTM)
++ }
++ Method(_STM, 0x3, NotSerialized) {}
++
++ Device(PMST) {
++ Name(_ADR, 0)
++ Method(_STA,0) {
++ if (LGreater(P0IS,0)) {
++ return (0x0F) /* sata is visible */
++ } else {
++ return (0x00) /* sata is missing */
++ }
++ }
++ }/* end of PMST */
++
++ Device(PSLA)
++ {
++ Name(_ADR, 1)
++ Method(_STA,0) {
++ if (LGreater(P1IS,0)) {
++ return (0x0F) /* sata is visible */
++ } else {
++ return (0x00) /* sata is missing */
++ }
++ }
++ } /* end of PSLA */
++} /* end of PMRY */
++
++
++Device(SEDY)
++{
++ Name(_ADR, 1) /* IDE Scondary Channel */
++ Method(_GTM, 0x0, NotSerialized) {
++ Return(STTM)
++ }
++ Method(_STM, 0x3, NotSerialized) {}
++
++ Device(SMST)
++ {
++ Name(_ADR, 0)
++ Method(_STA,0) {
++ if (LGreater(P2IS,0)) {
++ return (0x0F) /* sata is visible */
++ } else {
++ return (0x00) /* sata is missing */
++ }
++ }
++ } /* end of SMST */
++
++ Device(SSLA)
++ {
++ Name(_ADR, 1)
++ Method(_STA,0) {
++ if (LGreater(P3IS,0)) {
++ return (0x0F) /* sata is visible */
++ } else {
++ return (0x00) /* sata is missing */
++ }
++ }
++ } /* end of SSLA */
++} /* end of SEDY */
++
++/* SATA Hot Plug Support */
++Scope(\_GPE) {
++ Method(_L1F,0x0,NotSerialized) {
++ if (\_SB.P0PR) {
++ if (LGreater(\_SB.P0IS,0)) {
++ sleep(32)
++ }
++ Notify(\_SB.PCI0.SAT0.PMRY.PMST, 0x01) /* NOTIFY_DEVICE_CHECK */
++ store(one, \_SB.P0PR)
++ }
++
++ if (\_SB.P1PR) {
++ if (LGreater(\_SB.P1IS,0)) {
++ sleep(32)
++ }
++ Notify(\_SB.PCI0.SAT0.PMRY.PSLA, 0x01) /* NOTIFY_DEVICE_CHECK */
++ store(one, \_SB.P1PR)
++ }
++
++ if (\_SB.P2PR) {
++ if (LGreater(\_SB.P2IS,0)) {
++ sleep(32)
++ }
++ Notify(\_SB.PCI0.SAT0.SEDY.SMST, 0x01) /* NOTIFY_DEVICE_CHECK */
++ store(one, \_SB.P2PR)
++ }
++
++ if (\_SB.P3PR) {
++ if (LGreater(\_SB.P3IS,0)) {
++ sleep(32)
++ }
++ Notify(\_SB.PCI0.SAT0.SEDY.SSLA, 0x01) /* NOTIFY_DEVICE_CHECK */
++ store(one, \_SB.P3PR)
++ }
++ }
++}
+diff --git a/src/southbridge/amd/sb700/bootblock.c b/src/southbridge/amd/sb700/bootblock.c
+index 67e6434..8f722a8 100644
+--- a/src/southbridge/amd/sb700/bootblock.c
++++ b/src/southbridge/amd/sb700/bootblock.c
+@@ -1,6 +1,7 @@
+ /*
+ * This file is part of the coreboot project.
+ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -35,10 +36,17 @@
+ static void sb700_enable_rom(void)
+ {
+ u8 reg8;
++ u32 dword;
+ pci_devfn_t dev;
+
+ dev = PCI_DEV(0, 0x14, 3);
+
++ /* The LPC settings below work for SPI flash as well;
++ * the hardware does not distinguish between LPC and SPI flash ROM
++ * aside from offering additional side-channel access to SPI flash
++ * via a separate register-based interface.
++ */
++
+ /* Decode variable LPC ROM address ranges 1 and 2. */
+ reg8 = pci_io_read_config8(dev, 0x48);
+ reg8 |= (1 << 3) | (1 << 4);
+@@ -52,15 +60,41 @@ static void sb700_enable_rom(void)
+
+ /* LPC ROM address range 2: */
+ /*
+- * Enable LPC ROM range start at:
+- * 0xfff8(0000): 512KB
+- * 0xfff0(0000): 1MB
+- * 0xffe0(0000): 2MB
+- * 0xffc0(0000): 4MB
+- */
++ * Enable LPC ROM range start at:
++ * 0xfff8(0000): 512KB
++ * 0xfff0(0000): 1MB
++ * 0xffe0(0000): 2MB
++ * 0xffc0(0000): 4MB
++ * 0xff80(0000): 8MB
++ */
+ pci_io_write_config16(dev, 0x6c, 0x10000 - (CONFIG_COREBOOT_ROMSIZE_KB >> 6));
+ /* Enable LPC ROM range end at 0xffff(ffff). */
+ pci_io_write_config16(dev, 0x6e, 0xffff);
++
++ /* SB700 LPC Bridge 0x48h.
++ * Turn on all LPC IO Port decode enables
++ */
++ dword = pci_io_read_config32(dev, 0x44);
++ dword = 0xffffffff;
++ pci_io_write_config32(dev, 0x44, dword);
++
++ /* SB700 LPC Bridge 0x48h.
++ * BIT0: Port Enable for SuperIO 0x2E-0x2F
++ * BIT1: Port Enable for SuperIO 0x4E-0x4F
++ * BIT4: Port Enable for LPC ROM Address Arrage2 (0x68-0x6C)
++ * BIT6: Port Enable for RTC IO 0x70-0x73
++ * BIT21: Port Enable for Port 0x80
++ */
++ reg8 = pci_io_read_config8(dev, 0x48);
++ reg8 |= (1<<0) | (1<<1) | (1<<4) | (1<<6);
++ pci_io_write_config8(dev, 0x48, reg8);
++
++ /* SB700 LPC Bridge 0x4ah.
++ * BIT4: Port Enable for Port 0x80
++ */
++ reg8 = pci_io_read_config8(dev, 0x4a);
++ reg8 |= (1<<4);
++ pci_io_write_config8(dev, 0x4a, reg8);
+ }
+
+ static void bootblock_southbridge_init(void)
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index d25599e..de3fa97 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -395,6 +396,15 @@ static void sb700_devices_por_init(void)
+ byte |= (1 << 0);
+ pci_write_config8(dev, 0xd2, byte);
+
++ /* set auxiliary smbus iobase and enable controller */
++ pci_write_config32(dev, 0x58, SMBUS_AUX_IO_BASE | 1);
++
++ if (inw(SMBUS_IO_BASE) == 0xFF)
++ printk(BIOS_INFO, "sb700_devices_por_init(): Primary SMBUS controller I/O not found\n");
++
++ if (inw(SMBUS_AUX_IO_BASE) == 0xFF)
++ printk(BIOS_INFO, "sb700_devices_por_init(): Secondary SMBUS controller I/O not found\n");
++
+ /* KB2RstEnable */
+ pci_write_config8(dev, 0x40, 0x44);
+
+@@ -439,6 +449,14 @@ static void sb700_devices_por_init(void)
+ /*pci_write_config8(dev, 0x79, 0x4F); */
+ pci_write_config8(dev, 0x78, 0xFF);
+
++ if (IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA)) {
++ printk(BIOS_DEBUG, "sb700_devices_por_init(): Disabling ISA DMA support\n");
++ /* Disable LPC ISA DMA Capability */
++ byte = pci_read_config8(dev, 0x78);
++ byte &= ~(1 << 0);
++ pci_write_config8(dev, 0x78, byte);
++ }
++
+ /* Set smbus iospace enable, I don't know why write 0x04 into reg5 that is reserved */
+ pci_write_config16(dev, 0x4, 0x0407);
+
+diff --git a/src/southbridge/amd/sb700/lpc.c b/src/southbridge/amd/sb700/lpc.c
+index a39ec18..0cc1e8b 100644
+--- a/src/southbridge/amd/sb700/lpc.c
++++ b/src/southbridge/amd/sb700/lpc.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -45,6 +46,8 @@ static void lpc_init(device_t dev)
+ u32 dword;
+ device_t sm_dev;
+
++ printk(BIOS_SPEW, "%s\n", __func__);
++
+ /* Enable the LPC Controller */
+ sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
+ dword = pci_read_config32(sm_dev, 0x64);
+diff --git a/src/southbridge/amd/sb700/sm.c b/src/southbridge/amd/sb700/sm.c
+index f544c88..3e6ddf1 100644
+--- a/src/southbridge/amd/sb700/sm.c
++++ b/src/southbridge/amd/sb700/sm.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -57,11 +58,8 @@ static void sm_init(device_t dev)
+ printk(BIOS_INFO, "sm_init().\n");
+
+ rev = get_sb700_revision(dev);
+- ioapic_base = (void *)(pci_read_config32(dev, 0x74) & (0xffffffe0)); /* some like mem resource, but does not have enable bit */
+- /* Don't rename APIC ID */
+- /* TODO: We should call setup_ioapic() here. But kernel hangs if cpu is K8.
+- * We need to check out why and change back. */
+- clear_ioapic(ioapic_base);
++ ioapic_base = (void *)(pci_read_config32(dev, 0x74) & (0xffffffe0)); /* some like mem resource, but does not have enable bit */
++ setup_ioapic(ioapic_base, 0); /* Don't rename IOAPIC ID. */
+
+ /* 2.10 Interrupt Routing/Filtering */
+ dword = pci_read_config8(dev, 0x62);
+@@ -127,9 +125,10 @@ static void sm_init(device_t dev)
+ get_option(&on, "power_on_after_fail");
+ byte = pm_ioread(0x74);
+ byte &= ~0x03;
+- if (on) {
+- byte |= 2;
+- }
++ if (on == 1)
++ byte |= 0x1; /* Force power on */
++ else if (on == 2)
++ byte |= 0x2; /* Use last power state */
+ byte |= 1 << 2;
+ pm_iowrite(0x74, byte);
+ printk(BIOS_INFO, "set power %s after power fail\n", on ? "on" : "off");
+@@ -293,6 +292,10 @@ static void sm_init(device_t dev)
+ byte &= ~(1 << 1);
+ pm_iowrite(0x59, byte);
+
++ /* Enable SCI as irq9. */
++ outb(0x4, 0xC00);
++ outb(0x9, 0xC01);
++
+ printk(BIOS_INFO, "sm_init() end\n");
+
+ /* Enable NbSb virtual channel */
+@@ -371,7 +374,7 @@ static void sb700_sm_read_resources(device_t dev)
+ struct resource *res;
+
+ /* Get the normal pci resources of this device */
+- /* pci_dev_read_resources(dev); */
++ pci_dev_read_resources(dev);
+
+ /* apic */
+ res = new_resource(dev, 0x74);
+diff --git a/src/southbridge/amd/sb700/smbus.h b/src/southbridge/amd/sb700/smbus.h
+index d223fe7..34b4098 100644
+--- a/src/southbridge/amd/sb700/smbus.h
++++ b/src/southbridge/amd/sb700/smbus.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,8 +25,8 @@
+ #include "stddef.h"
+ #include <arch/io.h>
+
+-#define SMBUS_IO_BASE 0x6000 /* Is it a temporary SMBus I/O base address? */
+- /*SIZE 0x40 */
++#define SMBUS_IO_BASE 0xb00
++#define SMBUS_AUX_IO_BASE 0xb20
+
+ #define SMBHSTSTAT 0x0
+ #define SMBSLVSTAT 0x1
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0009-southbridge-amd-sr5650-Fix-boot-failure-on-ASUS-KGPE.patch b/resources/libreboot/patch/kgpe-d16/0009-southbridge-amd-sr5650-Fix-boot-failure-on-ASUS-KGPE.patch
new file mode 100644
index 00000000..3cffcccc
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0009-southbridge-amd-sr5650-Fix-boot-failure-on-ASUS-KGPE.patch
@@ -0,0 +1,619 @@
+From dd732a07e6aec5386eea9a00845f62c5b8b6ae68 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:46:38 -0500
+Subject: [PATCH 009/146] southbridge/amd/sr5650: Fix boot failure on ASUS
+ KGPE-D16
+
+---
+ src/southbridge/amd/sr5650/acpi/sr5650.asl | 388 ++++++++++++++++++++++++++++
+ src/southbridge/amd/sr5650/early_setup.c | 7 +-
+ src/southbridge/amd/sr5650/ht.c | 3 +-
+ src/southbridge/amd/sr5650/pcie.c | 37 ++-
+ src/southbridge/amd/sr5650/sr5650.c | 51 ++--
+ 5 files changed, 456 insertions(+), 30 deletions(-)
+ create mode 100644 src/southbridge/amd/sr5650/acpi/sr5650.asl
+
+diff --git a/src/southbridge/amd/sr5650/acpi/sr5650.asl b/src/southbridge/amd/sr5650/acpi/sr5650.asl
+new file mode 100644
+index 0000000..a6ab114
+--- /dev/null
++++ b/src/southbridge/amd/sr5650/acpi/sr5650.asl
+@@ -0,0 +1,388 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2009 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++Scope(\) {
++ Name(PCBA, 0xE0000000) /* Base address of PCIe config space */
++ Name(HPBA, 0xFED00000) /* Base address of HPET table */
++
++ /* PIC IRQ mapping registers, C00h-C01h */
++ OperationRegion(PRQM, SystemIO, 0x00000C00, 0x00000002)
++ Field(PRQM, ByteAcc, NoLock, Preserve) {
++ PRQI, 0x00000008,
++ PRQD, 0x00000008, /* Offset: 1h */
++ }
++ IndexField(PRQI, PRQD, ByteAcc, NoLock, Preserve) {
++ PINA, 0x00000008, /* Index 0 */
++ PINB, 0x00000008, /* Index 1 */
++ PINC, 0x00000008, /* Index 2 */
++ PIND, 0x00000008, /* Index 3 */
++ AINT, 0x00000008, /* Index 4 */
++ SINT, 0x00000008, /* Index 5 */
++ , 0x00000008, /* Index 6 */
++ AAUD, 0x00000008, /* Index 7 */
++ AMOD, 0x00000008, /* Index 8 */
++ PINE, 0x00000008, /* Index 9 */
++ PINF, 0x00000008, /* Index A */
++ PING, 0x00000008, /* Index B */
++ PINH, 0x00000008, /* Index C */
++ }
++
++ /* PCI Error control register */
++ OperationRegion(PERC, SystemIO, 0x00000C14, 0x00000001)
++ Field(PERC, ByteAcc, NoLock, Preserve) {
++ SENS, 0x00000001,
++ PENS, 0x00000001,
++ SENE, 0x00000001,
++ PENE, 0x00000001,
++ }
++
++ Scope(\_SB) {
++ /* PCIe Configuration Space for 16 busses */
++ OperationRegion(PCFG, SystemMemory, PCBA, 0x01000000) /* Each bus consumes 1MB */
++ Field(PCFG, ByteAcc, NoLock, Preserve) {
++ /* Byte offsets are computed using the following technique:
++ * ((bus number + 1) * ((device number * 8) * 4096)) + register offset
++ * The 8 comes from 8 functions per device, and 4096 bytes per function config space
++ */
++ Offset(0x00088024), /* Byte offset to SATA register 24h - Bus 0, Device 17, Function 0 */
++ STB5, 32,
++ Offset(0x00098042), /* Byte offset to OHCI0 register 42h - Bus 0, Device 19, Function 0 */
++ PT0D, 1,
++ PT1D, 1,
++ PT2D, 1,
++ PT3D, 1,
++ PT4D, 1,
++ PT5D, 1,
++ PT6D, 1,
++ PT7D, 1,
++ PT8D, 1,
++ PT9D, 1,
++ Offset(0x000A0004), /* Byte offset to SMBUS register 4h - Bus 0, Device 20, Function 0 */
++ SBIE, 1,
++ SBME, 1,
++ Offset(0x000A0008), /* Byte offset to SMBUS register 8h - Bus 0, Device 20, Function 0 */
++ SBRI, 8,
++ Offset(0x000A0014), /* Byte offset to SMBUS register 14h - Bus 0, Device 20, Function 0 */
++ SBB1, 32,
++ Offset(0x000A0078), /* Byte offset to SMBUS register 78h - Bus 0, Device 20, Function 0 */
++ ,14,
++ P92E, 1, /* Port92 decode enable */
++ }
++
++ OperationRegion(SB5, SystemMemory, STB5, 0x1000)
++ Field(SB5, AnyAcc, NoLock, Preserve){
++ /* Port 0 */
++ Offset(0x120), /* Port 0 Task file status */
++ P0ER, 1,
++ , 2,
++ P0DQ, 1,
++ , 3,
++ P0BY, 1,
++ Offset(0x128), /* Port 0 Serial ATA status */
++ P0DD, 4,
++ , 4,
++ P0IS, 4,
++ Offset(0x12C), /* Port 0 Serial ATA control */
++ P0DI, 4,
++ Offset(0x130), /* Port 0 Serial ATA error */
++ , 16,
++ P0PR, 1,
++
++ /* Port 1 */
++ offset(0x1A0), /* Port 1 Task file status */
++ P1ER, 1,
++ , 2,
++ P1DQ, 1,
++ , 3,
++ P1BY, 1,
++ Offset(0x1A8), /* Port 1 Serial ATA status */
++ P1DD, 4,
++ , 4,
++ P1IS, 4,
++ Offset(0x1AC), /* Port 1 Serial ATA control */
++ P1DI, 4,
++ Offset(0x1B0), /* Port 1 Serial ATA error */
++ , 16,
++ P1PR, 1,
++
++ /* Port 2 */
++ Offset(0x220), /* Port 2 Task file status */
++ P2ER, 1,
++ , 2,
++ P2DQ, 1,
++ , 3,
++ P2BY, 1,
++ Offset(0x228), /* Port 2 Serial ATA status */
++ P2DD, 4,
++ , 4,
++ P2IS, 4,
++ Offset(0x22C), /* Port 2 Serial ATA control */
++ P2DI, 4,
++ Offset(0x230), /* Port 2 Serial ATA error */
++ , 16,
++ P2PR, 1,
++
++ /* Port 3 */
++ Offset(0x2A0), /* Port 3 Task file status */
++ P3ER, 1,
++ , 2,
++ P3DQ, 1,
++ , 3,
++ P3BY, 1,
++ Offset(0x2A8), /* Port 3 Serial ATA status */
++ P3DD, 4,
++ , 4,
++ P3IS, 4,
++ Offset(0x2AC), /* Port 3 Serial ATA control */
++ P3DI, 4,
++ Offset(0x2B0), /* Port 3 Serial ATA error */
++ , 16,
++ P3PR, 1,
++ }
++
++ Method(CIRQ, 0x00, NotSerialized){
++ Store(0, PINA)
++ Store(0, PINB)
++ Store(0, PINC)
++ Store(0, PIND)
++ Store(0, PINE)
++ Store(0, PINF)
++ Store(0, PING)
++ Store(0, PINH)
++ }
++
++ /* set "A", 8259 interrupts */
++ Name (PRSA, ResourceTemplate () {
++ IRQ(Level, ActiveLow, Exclusive) {4, 7, 10, 11, 12, 14, 15}
++ })
++
++ Method (CRSA, 1, Serialized) {
++ Name (LRTL, ResourceTemplate() {
++ IRQ(Level, ActiveLow, Shared) {15}
++ })
++ CreateWordField(LRTL, 1, LIRQ)
++ ShiftLeft(1, Arg0, LIRQ)
++ Return (LRTL)
++ }
++
++ Method (SRSA, 1, Serialized) {
++ CreateWordField(Arg0, 1, LIRQ)
++ FindSetRightBit(LIRQ, Local0)
++ if (Local0) {
++ Decrement(Local0)
++ }
++ Return (Local0)
++ }
++
++ Device(LNKA) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 1)
++ Method(_STA, 0) {
++ if (PINA) {
++ Return(0x0B) /* LNKA is invisible */
++ } else {
++ Return(0x09) /* LNKA is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINA)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINA))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINA)
++ }
++ }
++
++ Device(LNKB) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 2)
++ Method(_STA, 0) {
++ if (PINB) {
++ Return(0x0B) /* LNKB is invisible */
++ } else {
++ Return(0x09) /* LNKB is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINB)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINB))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINB)
++ }
++ }
++
++ Device(LNKC) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 3)
++ Method(_STA, 0) {
++ if (PINC) {
++ Return(0x0B) /* LNKC is invisible */
++ } else {
++ Return(0x09) /* LNKC is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINC)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINC))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINC)
++ }
++ }
++
++ Device(LNKD) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 4)
++ Method(_STA, 0) {
++ if (PIND) {
++ Return(0x0B) /* LNKD is invisible */
++ } else {
++ Return(0x09) /* LNKD is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PIND)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PIND))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PIND)
++ }
++ }
++
++ Device(LNKE) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 5)
++ Method(_STA, 0) {
++ if (PINE) {
++ Return(0x0B) /* LNKE is invisible */
++ } else {
++ Return(0x09) /* LNKE is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINE)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINE))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINE)
++ }
++ }
++
++ Device(LNKF) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 6)
++ Method(_STA, 0) {
++ if (PINF) {
++ Return(0x0B) /* LNKF is invisible */
++ } else {
++ Return(0x09) /* LNKF is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINF)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINF))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINF)
++ }
++ }
++
++ Device(LNKG) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 7)
++ Method(_STA, 0) {
++ if (PING) {
++ Return(0x0B) /* LNKG is invisible */
++ } else {
++ Return(0x09) /* LNKG is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PING)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PING))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PING)
++ }
++ }
++
++ Device(LNKH) {
++ Name(_HID, EISAID("PNP0C0F"))
++ Name(_UID, 8)
++ Method(_STA, 0) {
++ if (PINH) {
++ Return(0x0B) /* LNKH is invisible */
++ } else {
++ Return(0x09) /* LNKH is disabled */
++ }
++ }
++ Method(_DIS, 0) {
++ Store(0, PINH)
++ }
++ Method(_PRS, 0) {
++ Return (PRSA)
++ }
++ Method (_CRS, 0, Serialized) {
++ Return (CRSA(PINH))
++ }
++ Method (_SRS, 1, Serialized) {
++ Store (SRSA(Arg0), PINH)
++ }
++ }
++
++ } /* End Scope(_SB) */
++
++} /* End Scope(/) */
+diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
+index ec555f8..664f60a 100644
+--- a/src/southbridge/amd/sr5650/early_setup.c
++++ b/src/southbridge/amd/sr5650/early_setup.c
+@@ -3,6 +3,7 @@
+ *
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -504,7 +505,8 @@ void sr5650_early_setup(void)
+ /*ATINB_PrepareInit */
+ get_cpu_rev();
+
+- switch (get_nb_rev(nb_dev)) { /* PCIEMiscInit */
++ uint8_t revno = get_nb_rev(nb_dev);
++ switch (revno) { /* PCIEMiscInit */
+ case REV_SR5650_A11:
+ printk(BIOS_INFO, "NB Revision is A11.\n");
+ break;
+@@ -514,6 +516,9 @@ void sr5650_early_setup(void)
+ case REV_SR5650_A21:
+ printk(BIOS_INFO, "NB Revision is A21.\n");
+ break;
++ default:
++ printk(BIOS_INFO, "NB Revision is %02x (Unrecognized).\n", revno);
++ break;
+ }
+
+ fam10_optimization();
+diff --git a/src/southbridge/amd/sr5650/ht.c b/src/southbridge/amd/sr5650/ht.c
+index c497107..02f4f7f 100644
+--- a/src/southbridge/amd/sr5650/ht.c
++++ b/src/southbridge/amd/sr5650/ht.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -55,7 +56,7 @@ static const apic_device_info default_apic_device_info_t [] = {
+ [13] = {4, ABCD, 30} /* Dev13 Grp4 [Int - 16..19] */
+ };
+
+-/* Their name are quite regular. So I undefine them. */
++/* These define names are common, so undefine them to avoid potential issues in other code */
+ #undef ABCD
+ #undef BCDA
+ #undef CDAB
+diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
+index 3720a61..d306b5a 100644
+--- a/src/southbridge/amd/sr5650/pcie.c
++++ b/src/southbridge/amd/sr5650/pcie.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -61,8 +62,10 @@ static void ValidatePortEn(device_t nb_dev)
+ *****************************************************************/
+ static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port)
+ {
++ printk(BIOS_DEBUG, "PciePowerOffGppPorts() port %d\n", port);
+ u32 reg;
+ u16 state_save;
++ uint8_t i;
+ struct southbridge_amd_sr5650_config *cfg =
+ (struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
+ u16 state = cfg->port_enable;
+@@ -72,6 +75,28 @@ static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port)
+ state = ~state;
+ state &= (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7);
+ state_save = state << 17;
++ /* Disable ports any that failed training */
++ for (i = 9; i <= 13; i++) {
++ if (!(AtiPcieCfg.PortDetect & 1 << i)) {
++ if ((port >= 9) && (port <= 13)) {
++ state |= (1 << (port + 7));
++ }
++ if (port == 9)
++ state_save |= 1 << 25;
++ if (port == 10)
++ state_save |= 1 << 26;
++ if (port == 11)
++ state_save |= 1 << 6;
++ if (port == 12)
++ state_save |= 1 << 7;
++
++ if (port == 13) {
++ reg = nbmisc_read_index(nb_dev, 0x2a);
++ reg |= 1 << 4;
++ nbmisc_write_index(nb_dev, 0x2a, reg);
++ }
++ }
++ }
+ state &= !(AtiPcieCfg.PortHp);
+ reg = nbmisc_read_index(nb_dev, 0x0c);
+ reg |= state;
+@@ -483,6 +508,8 @@ static void EnableLclkGating(device_t dev)
+ *****************************************/
+ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
+ {
++ uint8_t training_ok = 1;
++
+ u32 gpp_sb_sel = 0;
+ struct southbridge_amd_sr5650_config *cfg =
+ (struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
+@@ -701,6 +728,12 @@ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
+ printk(BIOS_DEBUG, "PcieTrainPort port=0x%x result=%d\n", port, res);
+ if (res) {
+ AtiPcieCfg.PortDetect |= 1 << port;
++ } else {
++ /* If the training failed the disable the bridge to prevent subsequent
++ * lockup on bridge configuration register read during the PCI bus scan
++ */
++ training_ok = 0;
++ dev->enabled = 0;
+ }
+ }
+ }
+@@ -747,8 +780,8 @@ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
+ * wait dev 0x6B bit3 clear
+ */
+
+- if (port == 8){
+- PciePowerOffGppPorts(nb_dev, dev, port); /* , This should be run for all ports that are not hotplug and don't detect devices */
++ if ((port == 8) || (!training_ok)) {
++ PciePowerOffGppPorts(nb_dev, dev, port); /* This is run for all ports that are not hotplug and don't detect devices */
+ }
+ }
+
+diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
+index 441be66..75383de 100644
+--- a/src/southbridge/amd/sr5650/sr5650.c
++++ b/src/southbridge/amd/sr5650/sr5650.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -95,32 +96,30 @@ void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data)
+ void ProgK8TempMmioBase(u8 in_out, u32 pcie_base_add, u32 mmio_base_add)
+ {
+ /* K8 Function1 is address map */
+- device_t k8_f1;
+- device_t np = dev_find_slot(0, PCI_DEVFN(0x19, 1));
+- u16 node;
+-
+- for (node = 0; node < CONFIG_MAX_PHYSICAL_CPUS; node++) {
+- k8_f1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
+- if (!k8_f1) {
+- break;
+- }
+-
+- if (in_out) {
+- /* Fill MMIO limit/base pair. */
+- pci_write_config32(k8_f1, 0xbc,
+- (((pcie_base_add + 0x10000000 -
+- 1) >> 8) & 0xffffff00) | 0x8 | (np ? 2 << 4 : 0 << 4));
+- pci_write_config32(k8_f1, 0xb8, (pcie_base_add >> 8) | 0x3);
+- pci_write_config32(k8_f1, 0xb4,
+- ((mmio_base_add + 0x10000000 -
+- 1) >> 8) | (np ? 2 << 4 : 0 << 4));
+- pci_write_config32(k8_f1, 0xb0, (mmio_base_add >> 8) | 0x3);
+- } else {
+- pci_write_config32(k8_f1, 0xb8, 0);
+- pci_write_config32(k8_f1, 0xbc, 0);
+- pci_write_config32(k8_f1, 0xb0, 0);
+- pci_write_config32(k8_f1, 0xb4, 0);
+- }
++ device_t k8_f1 = dev_find_slot(0, PCI_DEVFN(0x18, 1));
++ device_t k8_f0 = dev_find_slot(0, PCI_DEVFN(0x18, 0));
++
++ if (in_out) {
++ u32 dword, sblk;
++
++ /* Get SBLink value (HyperTransport I/O Hub Link ID). */
++ dword = pci_read_config32(k8_f0, 0x64);
++ sblk = (dword >> 8) & 0x3;
++
++ /* Fill MMIO limit/base pair. */
++ pci_write_config32(k8_f1, 0xbc,
++ (((pcie_base_add + 0x10000000 -
++ 1) >> 8) & 0xffffff00) | 0x80 | (sblk << 4));
++ pci_write_config32(k8_f1, 0xb8, (pcie_base_add >> 8) | 0x3);
++ pci_write_config32(k8_f1, 0xb4,
++ (((mmio_base_add + 0x10000000 -
++ 1) >> 8) & 0xffffff00) | (sblk << 4));
++ pci_write_config32(k8_f1, 0xb0, (mmio_base_add >> 8) | 0x3);
++ } else {
++ pci_write_config32(k8_f1, 0xb8, 0);
++ pci_write_config32(k8_f1, 0xbc, 0);
++ pci_write_config32(k8_f1, 0xb0, 0);
++ pci_write_config32(k8_f1, 0xb4, 0);
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0010-cpu-amd-Add-initial-support-for-AMD-Socket-G34-proce.patch b/resources/libreboot/patch/kgpe-d16/0010-cpu-amd-Add-initial-support-for-AMD-Socket-G34-proce.patch
new file mode 100644
index 00000000..dadfa5a4
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0010-cpu-amd-Add-initial-support-for-AMD-Socket-G34-proce.patch
@@ -0,0 +1,788 @@
+From a7e9e5b7d4f3699e043574b2ad5d13ff65125fce Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:50:29 -0500
+Subject: [PATCH 010/146] cpu/amd: Add initial support for AMD Socket G34
+ processors
+
+---
+ src/cpu/amd/Kconfig | 1 +
+ src/cpu/amd/Makefile.inc | 1 +
+ src/cpu/amd/car/post_cache_as_ram.c | 19 ++++-
+ src/cpu/amd/model_10xxx/init_cpus.c | 34 ++++++++-
+ src/cpu/amd/model_10xxx/model_10xxx_init.c | 2 +
+ src/cpu/amd/model_10xxx/processor_name.c | 23 ++++++
+ src/cpu/amd/model_10xxx/ram_calc.c | 2 +
+ src/cpu/amd/quadcore/quadcore_id.c | 77 +++++++++++++++-----
+ src/cpu/amd/socket_G34/Kconfig | 29 ++++++++
+ src/cpu/amd/socket_G34/Makefile.inc | 14 ++++
+ src/cpu/amd/socket_G34/socket_G34.c | 25 +++++++
+ src/northbridge/amd/amdfam10/northbridge.c | 102 ++++++++++++++++++++++-----
+ src/northbridge/amd/amdht/ht_wrapper.c | 105 +++++++++++++++++++++++++++-
+ 13 files changed, 390 insertions(+), 44 deletions(-)
+ create mode 100644 src/cpu/amd/socket_G34/Kconfig
+ create mode 100644 src/cpu/amd/socket_G34/Makefile.inc
+ create mode 100644 src/cpu/amd/socket_G34/socket_G34.c
+
+diff --git a/src/cpu/amd/Kconfig b/src/cpu/amd/Kconfig
+index 8286b2a..3a02043 100644
+--- a/src/cpu/amd/Kconfig
++++ b/src/cpu/amd/Kconfig
+@@ -5,6 +5,7 @@ source src/cpu/amd/socket_AM2/Kconfig
+ source src/cpu/amd/socket_AM2r2/Kconfig
+ source src/cpu/amd/socket_AM3/Kconfig
+ source src/cpu/amd/socket_C32/Kconfig
++source src/cpu/amd/socket_G34/Kconfig
+ source src/cpu/amd/socket_ASB2/Kconfig
+ source src/cpu/amd/socket_F/Kconfig
+ source src/cpu/amd/socket_F_1207/Kconfig
+diff --git a/src/cpu/amd/Makefile.inc b/src/cpu/amd/Makefile.inc
+index a73e25f..e532aba 100644
+--- a/src/cpu/amd/Makefile.inc
++++ b/src/cpu/amd/Makefile.inc
+@@ -8,6 +8,7 @@ subdirs-$(CONFIG_CPU_AMD_SOCKET_AM2R2) += socket_AM2r2
+ subdirs-$(CONFIG_CPU_AMD_SOCKET_AM3) += socket_AM3
+ subdirs-$(CONFIG_CPU_AMD_SOCKET_ASB2) += socket_ASB2
+ subdirs-$(CONFIG_CPU_AMD_SOCKET_C32_NON_AGESA) += socket_C32
++subdirs-$(CONFIG_CPU_AMD_SOCKET_G34_NON_AGESA) += socket_G34
+ subdirs-$(CONFIG_CPU_AMD_GEODE_GX2) += geode_gx2
+ subdirs-$(CONFIG_CPU_AMD_GEODE_LX) += geode_lx
+ subdirs-$(CONFIG_CPU_AMD_SOCKET_S1G1) += socket_S1G1
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index 96df3e7..230d1aa 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -1,4 +1,5 @@
+-/* 2005.6 by yhlu
++/* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * 2005.6 by yhlu
+ * 2006.3 yhlu add copy data from CAR to ram
+ */
+ #include <string.h>
+@@ -46,6 +47,15 @@ static void memset_(void *d, int val, size_t len)
+ memset(d, val, len);
+ }
+
++static int memcmp_(void *d, const void *s, size_t len)
++{
++#if PRINTK_IN_CAR
++ printk(BIOS_SPEW, " Compare [%08x-%08x] with [%08x - %08x] ... ",
++ (u32) s, (u32) (s + len - 1), (u32) d, (u32) (d + len - 1));
++#endif
++ return memcmp(d, s, len);
++}
++
+ static void prepare_romstage_ramstack(void *resume_backup_memory)
+ {
+ size_t backup_top = backup_size();
+@@ -110,6 +120,12 @@ void post_cache_as_ram(void)
+ memcpy_(migrated_car, &_car_data_start[0], car_size);
+ print_car_debug("Done\n");
+
++ print_car_debug("Verifying data integrity in RAM... ");
++ if (memcmp_(migrated_car, &_car_data_start[0], car_size) == 0)
++ print_car_debug("Done\n");
++ else
++ print_car_debug("FAILED\n");
++
+ /* New stack grows right below migrated_car. */
+ print_car_debug("Switching to use RAM as stack... ");
+ cache_as_ram_switch_stack(migrated_car);
+@@ -128,6 +144,7 @@ void cache_as_ram_new_stack (void)
+ disable_cache_as_ram_bsp();
+
+ disable_cache();
++ /* Enable cached access to RAM in the range 1M to CONFIG_RAMTOP */
+ set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK);
+ enable_cache();
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 4c72848..8de6d25 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -67,6 +68,9 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ u32 nb_cfg_54;
+ int i, j;
+ u32 ApicIdCoreIdSize;
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
+
+ /* get_nodes define in ht_wrapper.c */
+ nodes = get_nodes();
+@@ -81,6 +85,16 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ /* Assume that all node are same stepping, otherwise we can use use
+ nb_cfg_54 from bsp for all nodes */
+ nb_cfg_54 = read_nb_cfg_54();
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++
++ if (cpuid_eax(0x80000001) >= 0x8)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
+
+ ApicIdCoreIdSize = (cpuid_ecx(0x80000008) >> 12 & 0xf);
+ if (ApicIdCoreIdSize) {
+@@ -91,6 +105,8 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+
+ for (i = 0; i < nodes; i++) {
+ cores_found = get_core_num_in_bsp(i);
++ if (siblings > cores_found)
++ siblings = cores_found;
+
+ u32 jstart, jend;
+
+@@ -107,9 +123,21 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ }
+
+ for (j = jstart; j <= jend; j++) {
+- ap_apicid =
+- i * (nb_cfg_54 ? (siblings + 1) : 1) +
+- j * (nb_cfg_54 ? 1 : 64);
++ if (dual_node) {
++ ap_apicid = 0;
++ if (nb_cfg_54) {
++ ap_apicid |= ((i >> 1) & 0x3) << 4; /* Node ID */
++ ap_apicid |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */
++ } else {
++ ap_apicid |= i & 0x3; /* Node ID */
++ ap_apicid |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */
++ }
++ } else {
++ ap_apicid =
++ i * (nb_cfg_54 ? (siblings + 1) : 1) +
++ j * (nb_cfg_54 ? 1 : 64);
++ }
++
+
+ #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET > 0)
+ #if !CONFIG_LIFT_BSP_APIC_ID
+diff --git a/src/cpu/amd/model_10xxx/model_10xxx_init.c b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+index 590b89d..b942c1a 100644
+--- a/src/cpu/amd/model_10xxx/model_10xxx_init.c
++++ b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -153,6 +154,7 @@ static struct cpu_device_id cpu_table[] = {
+ { X86_VENDOR_AMD, 0x100F63 }, /* DA-C3 */
+ { X86_VENDOR_AMD, 0x100F80 }, /* HY-D0 */
+ { X86_VENDOR_AMD, 0x100F81 }, /* HY-D1 */
++ { X86_VENDOR_AMD, 0x100F91 }, /* HY-D1 */
+ { X86_VENDOR_AMD, 0x100FA0 }, /* PH-E0 */
+ { 0, 0 },
+ };
+diff --git a/src/cpu/amd/model_10xxx/processor_name.c b/src/cpu/amd/model_10xxx/processor_name.c
+index a25e3a9..12c45c9 100644
+--- a/src/cpu/amd/model_10xxx/processor_name.c
++++ b/src/cpu/amd/model_10xxx/processor_name.c
+@@ -157,6 +157,24 @@ static const struct str_s String2_socket_AM2[] = {
+ {0, 0, 0, NULL}
+ };
+
++static const struct str_s String1_socket_G34[] = {
++ {0x00, 0x07, 0x00, "AMD Opteron(tm) Processor 61"},
++ {0x00, 0x0B, 0x00, "AMD Opteron(tm) Processor 61"},
++ {0x01, 0x07, 0x01, "Embedded AMD Opteron(tm) Processor "},
++ {0, 0, 0, NULL}
++};
++
++static const struct str_s String2_socket_G34[] = {
++ {0x00, 0x07, 0x00, " HE"},
++ {0x00, 0x07, 0x01, " SE"},
++ {0x00, 0x0B, 0x00, " HE"},
++ {0x00, 0x0B, 0x01, " SE"},
++ {0x00, 0x0B, 0x0F, ""},
++ {0x01, 0x07, 0x01, " QS"},
++ {0x01, 0x07, 0x02, " KS"},
++ {0, 0, 0, NULL}
++};
++
+ static const struct str_s String1_socket_C32[] = {
+ {0x00, 0x03, 0x00, "AMD Opteron(tm) Processor 41"},
+ {0x00, 0x05, 0x00, "AMD Opteron(tm) Processor 41"},
+@@ -240,6 +258,11 @@ int init_processor_name(void)
+ str = String1_socket_AM2;
+ str2 = String2_socket_AM2;
+ break;
++ case 3: /* G34 */
++ str = String1_socket_G34;
++ str2 = String2_socket_G34;
++ str2_checkNC = 0;
++ break;
+ case 5: /* C32 */
+ str = String1_socket_C32;
+ str2 = String2_socket_C32;
+diff --git a/src/cpu/amd/model_10xxx/ram_calc.c b/src/cpu/amd/model_10xxx/ram_calc.c
+index c8637c9..46ccdbd 100644
+--- a/src/cpu/amd/model_10xxx/ram_calc.c
++++ b/src/cpu/amd/model_10xxx/ram_calc.c
+@@ -26,6 +26,7 @@
+
+ #include "ram_calc.h"
+
++#if !IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
+ uint64_t get_uma_memory_size(uint64_t topmem)
+ {
+ uint64_t uma_size = 0;
+@@ -50,3 +51,4 @@ void *cbmem_top(void)
+
+ return (void *) topmem - get_uma_memory_size(topmem);
+ }
++#endif
+diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c
+index cf45196..778e96f 100644
+--- a/src/cpu/amd/quadcore/quadcore_id.c
++++ b/src/cpu/amd/quadcore/quadcore_id.c
+@@ -1,6 +1,7 @@
+ /*
+ * This file is part of the coreboot project.
+ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -37,33 +38,71 @@ u32 get_initial_apicid(void)
+ return ((cpuid_ebx(1) >> 24) & 0xff);
+ }
+
+-//called by amd_siblings too
+-#define CORE_ID_BIT 2
+-#define NODE_ID_BIT 6
++/* Called by amd_siblings (ramstage) as well */
+ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ {
+ struct node_core_id id;
+- u32 core_id_bits;
++ uint8_t apicid;
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
+
+- u32 ApicIdCoreIdSize = (cpuid_ecx(0x80000008)>>12 & 0xf);
+- if(ApicIdCoreIdSize) {
+- core_id_bits = ApicIdCoreIdSize;
+- } else {
+- core_id_bits = CORE_ID_BIT; //quad core
+- }
++#ifdef __PRE_RAM__
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++#else
++ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
++#endif
++
++ if (cpuid_eax(0x80000001) >= 0x8)
++ /* Revision D or later */
++ rev_gte_d = 1;
+
+- // get the apicid via cpuid(1) ebx[31:24]
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
++ /* Get the apicid via cpuid(1) ebx[31:24]
++ * The apicid format varies based on processor revision
++ */
++ apicid = (cpuid_ebx(1) >> 24) & 0xff;
+ if( nb_cfg_54) {
+- // when NB_CFG[54] is set, nodeid = ebx[31:26], coreid = ebx[25:24]
+- id.coreid = (cpuid_ebx(1) >> 24) & 0xff;
+- id.nodeid = (id.coreid>>core_id_bits);
+- id.coreid &= ((1<<core_id_bits)-1);
++ if (rev_gte_d && dual_node) {
++ id.coreid = apicid & 0xf;
++ id.nodeid = (apicid & 0x30) >> 4;
++ } else if (rev_gte_d && !dual_node) {
++ id.coreid = apicid & 0x7;
++ id.nodeid = (apicid & 0x38) >> 3;
++ } else {
++ id.coreid = apicid & 0x3;
++ id.nodeid = (apicid & 0x1c) >> 2;
++ }
+ } else {
+- // when NB_CFG[54] is clear, nodeid = ebx[29:24], coreid = ebx[31:30]
+- id.nodeid = (cpuid_ebx(1) >> 24) & 0xff;
+- id.coreid = (id.nodeid>>NODE_ID_BIT);
+- id.nodeid &= ((1<<NODE_ID_BIT)-1);
++ if (rev_gte_d && dual_node) {
++ id.coreid = (apicid & 0xf0) >> 4;
++ id.nodeid = apicid & 0x3;
++ } else if (rev_gte_d && !dual_node) {
++ id.coreid = (apicid & 0xe0) >> 5;
++ id.nodeid = apicid & 0x7;
++ } else {
++ id.coreid = (apicid & 0x60) >> 5;
++ id.nodeid = apicid & 0x7;
++ }
+ }
++
++ if (rev_gte_d) {
++ /* Coreboot expects each separate processor die to be on a different nodeid.
++ * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
++ */
++ uint8_t core_count = (((f3xe8 & 0x00008000) >> 13) | ((f3xe8 & 0x00003000) >> 12)) + 1;
++
++ id.nodeid = id.nodeid * 2;
++ if (id.coreid >= core_count) {
++ id.nodeid += 1;
++ id.coreid = id.coreid - core_count;
++ }
++ }
++
+ return id;
+ }
+
+diff --git a/src/cpu/amd/socket_G34/Kconfig b/src/cpu/amd/socket_G34/Kconfig
+new file mode 100644
+index 0000000..abc9726
+--- /dev/null
++++ b/src/cpu/amd/socket_G34/Kconfig
+@@ -0,0 +1,29 @@
++config CPU_AMD_SOCKET_G34_NON_AGESA
++ bool
++ select CPU_AMD_MODEL_10XXX
++ select PCI_IO_CFG_EXT
++ select X86_AMD_FIXED_MTRRS
++
++if CPU_AMD_SOCKET_G34_NON_AGESA
++
++config CPU_SOCKET_TYPE
++ hex
++ default 0x15
++
++config EXT_RT_TBL_SUPPORT
++ bool
++ default n
++
++config CBB
++ hex
++ default 0x0
++
++config CDB
++ hex
++ default 0x18
++
++config XIP_ROM_SIZE
++ hex
++ default 0x80000
++
++endif
+diff --git a/src/cpu/amd/socket_G34/Makefile.inc b/src/cpu/amd/socket_G34/Makefile.inc
+new file mode 100644
+index 0000000..a8e1333
+--- /dev/null
++++ b/src/cpu/amd/socket_G34/Makefile.inc
+@@ -0,0 +1,14 @@
++ramstage-y += socket_G34.c
++subdirs-y += ../model_10xxx
++subdirs-y += ../quadcore
++subdirs-y += ../mtrr
++subdirs-y += ../microcode
++subdirs-y += ../../x86/tsc
++subdirs-y += ../../x86/lapic
++subdirs-y += ../../x86/cache
++subdirs-y += ../../x86/pae
++subdirs-y += ../../x86/mtrr
++subdirs-y += ../../x86/smm
++subdirs-y += ../smm
++
++cpu_incs-y += $(src)/cpu/amd/car/cache_as_ram.inc
+diff --git a/src/cpu/amd/socket_G34/socket_G34.c b/src/cpu/amd/socket_G34/socket_G34.c
+new file mode 100644
+index 0000000..90f7b8c
+--- /dev/null
++++ b/src/cpu/amd/socket_G34/socket_G34.c
+@@ -0,0 +1,25 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <device/device.h>
++
++struct chip_operations cpu_amd_socket_G34_ops = {
++ CHIP_NAME("socket G34")
++};
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 6d91cbd..7bd8675 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -187,6 +187,43 @@ static void ht_route_link(struct bus *link, scan_state mode)
+ }
+ }
+
++static void amd_g34_fixup(struct bus *link, device_t dev)
++{
++ uint32_t nodeid = amdfam10_nodeid(dev);
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
++
++ if (cpuid_eax(0x80000001) >= 0x8)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d) {
++ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
++
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
++ if (dual_node) {
++ /* Each G34 processor contains a defective HT link.
++ * See the BKDG Rev 3.62 section 2.7.1.5 for details.
++ */
++ f3xe8 = pci_read_config32(get_node_pci(nodeid, 3), 0xe8);
++ uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
++ if (internal_node_number == 0) {
++ /* Node 0 */
++ if (link->link_num == 6) /* Link 2 Sublink 1 */
++ printk(BIOS_DEBUG, "amdfam10_scan_chain(): node %d (internal node ID %d): skipping defective HT link\n", nodeid, internal_node_number);
++ } else {
++ /* Node 1 */
++ if (link->link_num == 5) /* Link 1 Sublink 1 */
++ printk(BIOS_DEBUG, "amdfam10_scan_chain(): node %d (internal node ID %d): skipping defective HT link\n", nodeid, internal_node_number);
++ }
++ }
++ }
++}
++
+ static void amdfam10_scan_chain(struct bus *link)
+ {
+ unsigned int next_unitid;
+@@ -277,8 +314,11 @@ static void amdfam10_scan_chains(device_t dev)
+ trim_ht_chain(dev);
+
+ for (link = dev->link_list; link; link = link->next) {
+- if (link->ht_link_up)
++ if (link->ht_link_up) {
++ if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX))
++ amd_g34_fixup(link, dev);
+ amdfam10_scan_chain(link);
++ }
+ }
+ }
+
+@@ -323,8 +363,7 @@ static struct resource *amdfam10_find_iopair(device_t dev, unsigned nodeid, unsi
+ if (result == 1) {
+ /* I have been allocated this one */
+ break;
+- }
+- else if (result > 1) {
++ } else if (result > 1) {
+ /* I have a free register pair */
+ free_reg = reg;
+ }
+@@ -357,8 +396,7 @@ static struct resource *amdfam10_find_mempair(device_t dev, u32 nodeid, u32 link
+ if (result == 1) {
+ /* I have been allocated this one */
+ break;
+- }
+- else if (result > 1) {
++ } else if (result > 1) {
+ /* I have a free register pair */
+ free_reg = reg;
+ }
+@@ -473,8 +511,7 @@ static void amdfam10_set_resource(device_t dev, struct resource *resource,
+
+ set_io_addr_reg(dev, nodeid, link_num, reg, rbase>>8, rend>>8);
+ store_conf_io_addr(nodeid, link_num, reg, (resource->index >> 24), rbase>>8, rend>>8);
+- }
+- else if (resource->flags & IORESOURCE_MEM) {
++ } else if (resource->flags & IORESOURCE_MEM) {
+ set_mmio_addr_reg(nodeid, link_num, reg, (resource->index >>24), rbase>>8, rend>>8, sysconf.nodes) ;// [39:8]
+ store_conf_mmio_addr(nodeid, link_num, reg, (resource->index >>24), rbase>>8, rend>>8);
+ }
+@@ -799,8 +836,7 @@ static void amdfam10_domain_set_resources(device_t dev)
+ }
+ if ((basek + sizek) <= 4*1024*1024) {
+ sizek = 0;
+- }
+- else {
++ } else {
+ basek = 4*1024*1024;
+ sizek -= (4*1024*1024 - mmio_basek);
+ }
+@@ -977,8 +1013,7 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ if (dimm_size_bytes > 0x800000000) {
+ t->size = 0x7FFF;
+ t->extended_size = dimm_size_bytes;
+- }
+- else {
++ } else {
+ t->size = dimm_size_bytes / (1024*1024);
+ t->size &= (~0x8000); /* size specified in megabytes */
+ }
+@@ -1005,8 +1040,7 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ t->part_number = smbios_add_string(t->eos, mem_info->dct_stat[node].DimmPartNumber[slot]);
+ if (mem_info->dct_stat[node].DimmSerialNumber[slot] == 0) {
+ t->serial_number = smbios_add_string(t->eos, "None");
+- }
+- else {
++ } else {
+ snprintf(string_buffer, sizeof (string_buffer), "%08X", mem_info->dct_stat[node].DimmSerialNumber[slot]);
+ t->serial_number = smbios_add_string(t->eos, string_buffer);
+ }
+@@ -1108,8 +1142,7 @@ static void add_more_links(device_t dev, unsigned total_links)
+ memset(link, 0, links*sizeof(*link));
+ last->next = link;
+ }
+- }
+- else {
++ } else {
+ link = malloc(total_links*sizeof(*link));
+ memset(link, 0, total_links*sizeof(*link));
+ dev->link_list = link;
+@@ -1244,6 +1277,10 @@ static void cpu_bus_scan(device_t dev)
+ unsigned busn, devn;
+ struct bus *pbus;
+
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
++
+ busn = CONFIG_CBB;
+ devn = CONFIG_CDB+i;
+ pbus = dev_mc->bus;
+@@ -1268,6 +1305,7 @@ static void cpu_bus_scan(device_t dev)
+ }
+ }
+
++
+ /* Ok, We need to set the links for that device.
+ * otherwise the device under it will not be scanned
+ */
+@@ -1279,6 +1317,17 @@ static void cpu_bus_scan(device_t dev)
+ if (cdb_dev)
+ add_more_links(cdb_dev, 4);
+
++ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
++
++ if (cpuid_eax(0x80000001) >= 0x8)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
+ cores_found = 0; // one core
+ cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3));
+ int enable_node = cdb_dev && cdb_dev->enabled;
+@@ -1290,6 +1339,9 @@ static void cpu_bus_scan(device_t dev)
+ printk(BIOS_DEBUG, " %s siblings=%d\n", dev_path(cdb_dev), cores_found);
+ }
+
++ if (siblings > cores_found)
++ siblings = cores_found;
++
+ u32 jj;
+ if(disable_siblings) {
+ jj = 0;
+@@ -1299,7 +1351,20 @@ static void cpu_bus_scan(device_t dev)
+ }
+
+ for (j = 0; j <=jj; j++ ) {
+- u32 apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
++ u32 apic_id;
++
++ if (dual_node) {
++ apic_id = 0;
++ if (nb_cfg_54) {
++ apic_id |= ((i >> 1) & 0x3) << 4; /* Node ID */
++ apic_id |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */
++ } else {
++ apic_id |= i & 0x3; /* Node ID */
++ apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */
++ }
++ } else {
++ apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
++ }
+
+ #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET>0)
+ if(sysconf.enabled_apic_ext_id) {
+@@ -1311,7 +1376,7 @@ static void cpu_bus_scan(device_t dev)
+ device_t cpu = add_cpu_device(cpu_bus, apic_id, enable_node);
+ if (cpu)
+ amd_cpu_topology(cpu, i, j);
+- } //j
++ }
+ }
+ }
+
+@@ -1356,8 +1421,7 @@ static void root_complex_enable_dev(struct device *dev)
+ /* Set the operations if it is a special bus type */
+ if (dev->path.type == DEVICE_PATH_DOMAIN) {
+ dev->ops = &pci_domain_ops;
+- }
+- else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
++ } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
+ dev->ops = &cpu_bus_ops;
+ }
+ }
+diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c
+index 36fe60b..bafda10 100644
+--- a/src/northbridge/amd/amdht/ht_wrapper.c
++++ b/src/northbridge/amd/amdht/ht_wrapper.c
+@@ -113,6 +113,20 @@ void getAmdTopolist(u8 ***p)
+ *p = (u8 **)amd_topo_list;
+ }
+
++/**
++ * BOOL AMD_CB_IgnoreLink(u8 Node, u8 Link)
++ * Description:
++ * This routine is used to ignore connected yet faulty HT links,
++ * such as those present in a G34 processor package.
++ *
++ * Parameters:
++ * @param[in] node = The node on which this chain is located
++ * @param[in] link = The link on the host for this chain
++ */
++static BOOL AMD_CB_IgnoreLink (u8 node, u8 link)
++{
++ return 0;
++}
+
+ /**
+ * void amd_ht_init(struct sys_info *sysinfo)
+@@ -128,7 +142,7 @@ static void amd_ht_init(struct sys_info *sysinfo)
+ 0, // u8 AutoBusStart;
+ 32, // u8 AutoBusMax;
+ 6, // u8 AutoBusIncrement;
+- NULL, // BOOL (*AMD_CB_IgnoreLink)();
++ AMD_CB_IgnoreLink, // BOOL (*AMD_CB_IgnoreLink)();
+ NULL, // BOOL (*AMD_CB_OverrideBusNumbers)();
+ AMD_CB_ManualBUIDSwapList, // BOOL (*AMD_CB_ManualBUIDSwapList)();
+ NULL, // void (*AMD_CB_DeviceCapOverride)();
+@@ -146,6 +160,93 @@ static void amd_ht_init(struct sys_info *sysinfo)
+ printk(BIOS_DEBUG, "Enter amd_ht_init()\n");
+ amdHtInitialize(&ht_wrapper);
+ printk(BIOS_DEBUG, "Exit amd_ht_init()\n");
++}
+
+-
++/**
++ * void amd_ht_fixup(struct sys_info *sysinfo)
++ *
++ * AMD HT fixup
++ *
++ */
++static void amd_ht_fixup(struct sys_info *sysinfo) {
++ printk(BIOS_DEBUG, "amd_ht_fixup()\n");
++ if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX)) {
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
++
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 16) | ((model & 0xf0) >> 4);
++
++ if (model >= 0x8)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d) {
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
++ if (dual_node) {
++ /* Each G34 processor contains a defective HT link.
++ * See the BKDG Rev 3.62 section 2.7.1.5 for details.
++ */
++ uint8_t node;
++ uint8_t node_count = get_nodes();
++ uint32_t dword;
++ for (node = 0; node < node_count; node++) {
++ f3xe8 = pci_read_config32(NODE_PCI(node, 3), 0xe8);
++ uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
++ printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link\n", node, internal_node_number);
++ if (internal_node_number == 0) {
++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xd8) & 0x1;
++ if (package_link_3_connected) {
++ /* Set WidthIn and WidthOut to 0 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0xc4);
++ dword &= ~0x77000000;
++ pci_write_config32(NODE_PCI(node, 0), 0xc4, dword);
++ /* Set Ganged to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0x178);
++ dword |= 0x00000001;
++ pci_write_config32(NODE_PCI(node, 0), 0x178, dword);
++ } else {
++ /* Set ConnDly to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
++ dword |= 0x00000100;
++ pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
++ /* Set TransOff and EndOfChain to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 4), 0xc4);
++ dword |= 0x000000c0;
++ pci_write_config32(NODE_PCI(node, 4), 0xc4, dword);
++ }
++ } else if (internal_node_number == 1) {
++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xb8) & 0x1;
++ if (package_link_3_connected) {
++ /* Set WidthIn and WidthOut to 0 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0xa4);
++ dword &= ~0x77000000;
++ pci_write_config32(NODE_PCI(node, 0), 0xa4, dword);
++ /* Set Ganged to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0x174);
++ dword |= 0x00000001;
++ pci_write_config32(NODE_PCI(node, 0), 0x174, dword);
++ } else {
++ /* Set ConnDly to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
++ dword |= 0x00000100;
++ pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
++ /* Set TransOff and EndOfChain to 1 */
++ dword = pci_read_config32(NODE_PCI(node, 4), 0xa4);
++ dword |= 0x000000c0;
++ pci_write_config32(NODE_PCI(node, 4), 0xa4, dword);
++ }
++ }
++ }
++ }
++ }
++ }
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0011-northbridge-amd-amdmct-Fix-broken-AMD-K10-DDR3-memor.patch b/resources/libreboot/patch/kgpe-d16/0011-northbridge-amd-amdmct-Fix-broken-AMD-K10-DDR3-memor.patch
new file mode 100644
index 00000000..ec822df4
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0011-northbridge-amd-amdmct-Fix-broken-AMD-K10-DDR3-memor.patch
@@ -0,0 +1,3451 @@
+From 791a6ea672f16f971422f10514bb0c4225930489 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 17:55:58 -0500
+Subject: [PATCH 011/146] northbridge/amd/amdmct: Fix broken AMD K10 DDR3
+ memory initalization
+
+---
+ src/northbridge/amd/amdmct/mct/mct_d.c | 1 -
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 177 ++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 8 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h | 87 +--
+ src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 806 ++++++++++++-----------
+ src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c | 14 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c | 3 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 19 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 5 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 803 +++++++++++-----------
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c | 18 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c | 13 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c | 7 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 42 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 267 ++++----
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 114 +---
+ 18 files changed, 1254 insertions(+), 1142 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct/mct_d.c b/src/northbridge/amd/amdmct/mct/mct_d.c
+index 3dec934..88910e2 100644
+--- a/src/northbridge/amd/amdmct/mct/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct/mct_d.c
+@@ -542,7 +542,6 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat = pDCTstatA + Node;
+ devx = pDCTstat->dev_map;
+ DramSelBaseAddr = 0;
+- pDCTstat = pDCTstatA + Node;
+ if (!pDCTstat->GangedMode) {
+ DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
+ /*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 71a6be8..fa59d71 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -214,6 +214,8 @@ static const u8 Table_DQSRcvEn_Offset[] = {0x00,0x01,0x10,0x11,0x2};
+ static const u8 Tab_L1CLKDis[] = {0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04};
+ static const u8 Tab_AM3CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
+ static const u8 Tab_S1CLKDis[] = {0xA2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
++static const u8 Tab_C32CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; /* Enable CS0 - CS3 clocks (DIMM0 - DIMM1) */
++static const u8 Tab_G34CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; /* Enable CS0 - CS3 clocks (DIMM0 - DIMM1) */
+ static const u8 Tab_ManualCLKDis[]= {0x10, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00};
+
+ static const u8 Table_Comp_Rise_Slew_20x[] = {7, 3, 2, 2, 0xFF};
+@@ -277,6 +279,11 @@ restartinit:
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
++
++ /* Zero out data structures to avoid false detection of DIMMs */
++ memset(pDCTstat, 0, sizeof(struct DCTStatStruc));
++
++ /* Initialize data structures */
+ pDCTstat->Node_ID = Node;
+ pDCTstat->dev_host = PA_HOST(Node);
+ pDCTstat->dev_map = PA_MAP(Node);
+@@ -284,17 +291,22 @@ restartinit:
+ pDCTstat->dev_nbmisc = PA_NBMISC(Node);
+ pDCTstat->NodeSysBase = node_sys_base;
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
+ mct_init(pMCTstat, pDCTstat);
+ mctNodeIDDebugPort_D();
+ pDCTstat->NodePresent = NodePresent_D(Node);
+ if (pDCTstat->NodePresent) { /* See if Node is there*/
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n");
+ clear_legacy_Mode(pMCTstat, pDCTstat);
+ pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n");
+ mct_InitialMCT_D(pMCTstat, pDCTstat);
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n");
+ mct_initDCT(pMCTstat, pDCTstat);
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+ goto fatalexit; /* any fatal errors?*/
+@@ -345,6 +357,7 @@ restartinit:
+
+ mct_FinalMCT_D(pMCTstat, pDCTstatA);
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
++
+ return;
+
+ fatalexit:
+@@ -560,7 +573,6 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat = pDCTstatA + Node;
+ devx = pDCTstat->dev_map;
+ DramSelBaseAddr = 0;
+- pDCTstat = pDCTstatA + Node; /* ??? */
+ if (!pDCTstat->GangedMode) {
+ DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
+ /*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
+@@ -645,6 +657,7 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+ devx = pDCTstat->dev_map;
+
+ if (pDCTstat->NodePresent) {
++ printk(BIOS_DEBUG, " Copy dram map from Node 0 to Node %02x \n", Node);
+ reg = 0x40; /*Dram Base 0*/
+ do {
+ val = Get_NB32(dev, reg);
+@@ -1162,7 +1175,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+
+ /* Program DRAM Timing values */
+ DramTimingLo = 0; /* Dram Timing Low init */
+- val = pDCTstat->CASL - 2; /* pDCTstat.CASL to reg. definition */
++ val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
+ DramTimingLo |= val;
+
+ val = pDCTstat->Trcd - Bias_TrcdT;
+@@ -1406,18 +1419,16 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ else if (tCKproposed16x <= 24) {
+ pDCTstat->TargetFreq = 6;
+ tCKproposed16x = 24;
+- }
+- else if (tCKproposed16x <= 30) {
++ } else if (tCKproposed16x <= 30) {
+ pDCTstat->TargetFreq = 5;
+ tCKproposed16x = 30;
+- }
+- else {
++ } else {
+ pDCTstat->TargetFreq = 4;
+ tCKproposed16x = 40;
+ }
+ /* Running through this loop twice:
+ - First time find tCL at target frequency
+- - Second tim find tCL at 400MHz */
++ - Second time find tCL at 400MHz */
+
+ for (;;) {
+ CLT_Fail = 0;
+@@ -1451,7 +1462,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ CLT_Fail = 1;
+ /* get CL and T */
+ if (!CLT_Fail) {
+- bytex = CLactual - 2;
++ bytex = CLactual;
+ if (tCKproposed16x == 20)
+ byte = 7;
+ else if (tCKproposed16x == 24)
+@@ -1632,7 +1643,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ val = 0x0f; /* recommended setting (default) */
+ DramConfigHi |= val << 24;
+
+- if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Bx))
++ if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx))
+ DramConfigHi |= 1 << DcqArbBypassEn;
+
+ /* Build MemClkDis Value from Dram Timing Lo and
+@@ -1657,6 +1668,10 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ p = Tab_L1CLKDis;
+ else if (byte == PT_M2 || byte == PT_AS)
+ p = Tab_AM3CLKDis;
++ else if (byte == PT_C3)
++ p = Tab_C32CLKDis;
++ else if (byte == PT_GR)
++ p = Tab_G34CLKDis;
+ else
+ p = Tab_S1CLKDis;
+
+@@ -2102,8 +2117,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ if (byte == JED_RDIMM || byte == JED_MiniRDIMM) {
+ RegDIMMPresent |= 1 << i;
+ pDCTstat->DimmRegistered[i] = 1;
+- }
+- else {
++ } else {
+ pDCTstat->DimmRegistered[i] = 0;
+ }
+ /* Check ECC capable */
+@@ -2977,9 +2991,9 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ } else { /* For Dx CPU */
+ val = 0x0CE00F00 | 1 << 29/* FlushWrOnStpGnt */;
+ if (!(pDCTstat->GangedMode))
+- val |= 0x20; /* MctWrLimit = 8 for Unganed mode */
++ val |= 0x20; /* MctWrLimit = 8 for Unganged mode */
+ else
+- val |= 0x40; /* MctWrLimit = 16 for ganed mode */
++ val |= 0x40; /* MctWrLimit = 16 for ganged mode */
+ Set_NB32(pDCTstat->dev_dct, 0x11C, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x1B0);
+@@ -3414,6 +3428,138 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ Set_NB32(dev, 0x98 + reg_off, 0x0D000030);
+ Set_NB32(dev, 0x9C + reg_off, dword);
+ Set_NB32(dev, 0x98 + reg_off, 0x4D040F30);
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[i];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++ uint32_t odt_pattern_0;
++ uint32_t odt_pattern_1;
++ uint32_t odt_pattern_2;
++ uint32_t odt_pattern_3;
++
++ /* Select appropriate ODT pattern for installed DIMMs
++ * Refer to the BKDG Rev. 3.62, page 120 onwards
++ */
++ if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) {
++ if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ if (rank_count_dimm1 == 1) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00020000;
++ } else if (rank_count_dimm1 == 2) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x02080000;
++ } else if (rank_count_dimm1 == 4) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x020a0000;
++ odt_pattern_3 = 0x080a0000;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ } else {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[i]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x01010202;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x09030603;
++ } else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4)) {
++ odt_pattern_0 = 0x01010000;
++ odt_pattern_1 = 0x01010a0a;
++ odt_pattern_2 = 0x01090000;
++ odt_pattern_3 = 0x01030e0b;
++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4)) {
++ odt_pattern_0 = 0x00000202;
++ odt_pattern_1 = 0x05050202;
++ odt_pattern_2 = 0x00000206;
++ odt_pattern_3 = 0x0d070203;
++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 == 4)) {
++ odt_pattern_0 = 0x05050a0a;
++ odt_pattern_1 = 0x05050a0a;
++ odt_pattern_2 = 0x050d0a0e;
++ odt_pattern_3 = 0x05070a0b;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ }
++ } else {
++ /* FIXME
++ * 3 DIMMs per channel UNIMPLEMENTED
++ */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ } else {
++ if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ if (rank_count_dimm1 == 1) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00020000;
++ } else if (rank_count_dimm1 == 2) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x02080000;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ } else {
++ /* 2 DIMMs detected */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x01010202;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x09030603;
++ }
++ } else {
++ /* FIXME
++ * 3 DIMMs per channel UNIMPLEMENTED
++ */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ }
++
++ /* Program ODT pattern */
++ Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x180, odt_pattern_1);
++ Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x181, odt_pattern_0);
++ Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x182, odt_pattern_3);
++ Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x183, odt_pattern_2);
+ }
+ }
+ }
+@@ -3657,6 +3803,7 @@ static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++/* Erratum 350 */
+ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+@@ -3692,11 +3839,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr); /* cache fills */
+
+ /* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */
+- Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00008000);
++ Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00008000);
+ mct_Wait(80); /* wait >= 300ns */
+
+ /* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
+- Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00000000);
++ Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00000000);
+ mct_Wait(800); /* wait >= 2us */
+ break;
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index e2d7aa8..219aa42 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -499,7 +499,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ /* CHB DIMM0 Byte 0 - 7 TxDqs */
+ /* CHB DIMM1 Byte 0 - 7 TxDqs */
+ /* CHB DIMM1 Byte 0 - 7 TxDqs */
+- u8 CH_D_B_RCVRDLY[2][4][8]; /* [A/B] [DIMM0-3] [DQS] */
++ u16 CH_D_B_RCVRDLY[2][4][8]; /* [A/B] [DIMM0-3] [DQS] */
+ /* CHA DIMM 0 Receiver Enable Delay*/
+ /* CHA DIMM 1 Receiver Enable Delay*/
+ /* CHA DIMM 2 Receiver Enable Delay*/
+@@ -509,7 +509,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ /* CHB DIMM 1 Receiver Enable Delay*/
+ /* CHB DIMM 2 Receiver Enable Delay*/
+ /* CHB DIMM 3 Receiver Enable Delay*/
+- u8 CH_D_BC_RCVRDLY[2][4];
++ u16 CH_D_BC_RCVRDLY[2][4];
+ /* CHA DIMM 0 - 4 Check Byte Receiver Enable Delay*/
+ /* CHB DIMM 0 - 4 Check Byte Receiver Enable Delay*/
+ u8 DIMMValidDCT[2]; /* DIMM# in DCT0*/
+@@ -769,7 +769,7 @@ u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass);
+ u32 SetupDqsPattern_1PassA(u8 Pass);
+ u32 SetupDqsPattern_1PassB(u8 Pass);
+ u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass);
+-u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
++u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u16 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
+ void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ u32 mctGetLogicalCPUID(u32 Node);
+@@ -779,7 +779,7 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTs
+ void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void TrainMaxReadLatency_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat,struct DCTStatStruc *pDCTstatA);
+-void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 FinalValue, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass);
++void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u8 FinalValue, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass);
+ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel);
+ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct);
+ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+index 60f98bc..c40ea1a 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -103,10 +104,10 @@ static void proc_CLFLUSH(u32 addr_hi)
+
+ __asm__ volatile (
+ /* clflush fs:[eax] */
+- "outb %%al, $0xed\n\t" /* _EXECFENCE */
+- "clflush %%fs:(%0)\n\t"
++ "outb %%al, $0xed\n\t" /* _EXECFENCE */
++ "clflush %%fs:(%0)\n\t"
+ "mfence\n\t"
+- ::"a" (addr_hi<<8)
++ ::"a" (addr_hi<<8)
+ );
+ }
+
+@@ -141,6 +142,24 @@ static u32 read32_fs(u32 addr_lo)
+ return value;
+ }
+
++static uint64_t read64_fs(uint32_t addr_lo)
++{
++ uint64_t value = 0;
++ uint32_t value_lo;
++ uint32_t value_hi;
++
++ __asm__ volatile (
++ "outb %%al, $0xed\n\t" /* _EXECFENCE */
++ "mfence\n\t"
++ "movl %%fs:(%2), %0\n\t"
++ "movl %%fs:(%3), %1\n\t"
++ :"=c"(value_lo), "=d"(value_hi): "a" (addr_lo), "b" (addr_lo + 4) : "memory"
++ );
++ value |= value_lo;
++ value |= ((uint64_t)value_hi) << 32;
++ return value;
++}
++
+ #ifdef UNUSED_CODE
+ static u8 read8_fs(u32 addr_lo)
+ {
+@@ -210,68 +229,6 @@ static __attribute__((noinline)) void FlushDQSTestPattern_L18(u32 addr_lo)
+ );
+ }
+
+-static void ReadL18TestPattern(u32 addr_lo)
+-{
+- /* set fs and use fs prefix to access the mem */
+- __asm__ volatile (
+- "outb %%al, $0xed\n\t" /* _EXECFENCE */
+- "movl %%fs:-128(%%esi), %%eax\n\t" /* TestAddr cache line */
+- "movl %%fs:-64(%%esi), %%eax\n\t" /* +1 */
+- "movl %%fs:(%%esi), %%eax\n\t" /* +2 */
+- "movl %%fs:64(%%esi), %%eax\n\t" /* +3 */
+-
+- "movl %%fs:-128(%%edi), %%eax\n\t" /* +4 */
+- "movl %%fs:-64(%%edi), %%eax\n\t" /* +5 */
+- "movl %%fs:(%%edi), %%eax\n\t" /* +6 */
+- "movl %%fs:64(%%edi), %%eax\n\t" /* +7 */
+-
+- "movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
+- "movl %%fs:-64(%%ebx), %%eax\n\t" /* +9 */
+- "movl %%fs:(%%ebx), %%eax\n\t" /* +10 */
+- "movl %%fs:64(%%ebx), %%eax\n\t" /* +11 */
+-
+- "movl %%fs:-128(%%ecx), %%eax\n\t" /* +12 */
+- "movl %%fs:-64(%%ecx), %%eax\n\t" /* +13 */
+- "movl %%fs:(%%ecx), %%eax\n\t" /* +14 */
+- "movl %%fs:64(%%ecx), %%eax\n\t" /* +15 */
+-
+- "movl %%fs:-128(%%edx), %%eax\n\t" /* +16 */
+- "movl %%fs:-64(%%edx), %%eax\n\t" /* +17 */
+- "mfence\n\t"
+-
+- :: "a"(0), "b" (addr_lo+128+8*64), "c" (addr_lo+128+12*64),
+- "d" (addr_lo +128+16*64), "S"(addr_lo+128),
+- "D"(addr_lo+128+4*64)
+- );
+-
+-}
+-
+-static void ReadL9TestPattern(u32 addr_lo)
+-{
+-
+- /* set fs and use fs prefix to access the mem */
+- __asm__ volatile (
+- "outb %%al, $0xed\n\t" /* _EXECFENCE */
+-
+- "movl %%fs:-128(%%ecx), %%eax\n\t" /* TestAddr cache line */
+- "movl %%fs:-64(%%ecx), %%eax\n\t" /* +1 */
+- "movl %%fs:(%%ecx), %%eax\n\t" /* +2 */
+- "movl %%fs:64(%%ecx), %%eax\n\t" /* +3 */
+-
+- "movl %%fs:-128(%%edx), %%eax\n\t" /* +4 */
+- "movl %%fs:-64(%%edx), %%eax\n\t" /* +5 */
+- "movl %%fs:(%%edx), %%eax\n\t" /* +6 */
+- "movl %%fs:64(%%edx), %%eax\n\t" /* +7 */
+-
+- "movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
+- "mfence\n\t"
+-
+- :: "a"(0), "b" (addr_lo+128+8*64), "c"(addr_lo+128),
+- "d"(addr_lo+128+4*64)
+- );
+-
+-}
+-
+ static void ReadMaxRdLat1CLTestPattern_D(u32 addr)
+ {
+ SetUpperFSbase(addr);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c
+index ae1654c..99a2628 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -17,7 +18,7 @@
+ * Foundation, Inc.
+ */
+
+-/* The socket type F (1207), Fr2, G (1207) are not tested.
++/* The socket type Fr2, G (1207) are not tested.
+ */
+
+ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+@@ -79,8 +80,7 @@ static void Get_ChannelPS_Cfg0_D( u8 MAAdimms, u8 Speed, u8 MAAload,
+ else
+ *AddrTmgCTL = 0x00353935;
+ }
+- }
+- else {
++ } else {
+ if(Speed == 4) {
+ *AddrTmgCTL = 0x00000000;
+ if (MAAdimms == 3)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 404727b..8572243 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -22,13 +23,6 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ u8 scale, u8 ChipSel);
+ static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel);
+-static u8 MiddleDQS_D(u8 min, u8 max);
+-static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start);
+-static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start);
+ static void WriteDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo);
+@@ -43,31 +37,19 @@ static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 addr_lo);
+ static void SetTargetWTIO_D(u32 TestAddr);
+ static void ResetTargetWTIO_D(void);
+-static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u32 TestAddr_lo);
+-static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 ChipSel,
+- u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
+ void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index);
+ u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 ChipSel);
+-static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start);
+ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel,
+ u8 receiver, u8 *valid);
+ static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 *buffer);
+-
+-static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 ChipSel,
+- u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
++static void proc_IOCLFLUSH_D(u32 addr_hi);
+
+ static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel);
+
+@@ -286,20 +268,99 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DQSDelay = (u8)DQSDelay;
+ }
+
++static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++{
++ uint32_t dword;
++
++ /* Lanes 0 - 3 */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8));
++ dword &= ~0x7f7f7f7f;
++ dword |= (delay[3] & 0x7f) << 24;
++ dword |= (delay[2] & 0x7f) << 16;
++ dword |= (delay[1] & 0x7f) << 8;
++ dword |= delay[0] & 0x7f;
++ Set_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8), dword);
++
++ /* Lanes 4 - 7 */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8));
++ dword &= ~0x7f7f7f7f;
++ dword |= (delay[7] & 0x7f) << 24;
++ dword |= (delay[6] & 0x7f) << 16;
++ dword |= (delay[5] & 0x7f) << 8;
++ dword |= delay[4] & 0x7f;
++ Set_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8), dword);
++
++ /* Lane 8 (ECC) */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8));
++ dword &= ~0x0000007f;
++ dword |= delay[8] & 0x7f;
++ Set_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8), dword);
++}
++
++static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++{
++ uint32_t dword;
++
++ /* Lanes 0 - 3 */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8));
++ dword &= ~0x3f3f3f3f;
++ dword |= (delay[3] & 0x3f) << 24;
++ dword |= (delay[2] & 0x3f) << 16;
++ dword |= (delay[1] & 0x3f) << 8;
++ dword |= delay[0] & 0x3f;
++ Set_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8), dword);
++
++ /* Lanes 4 - 7 */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8));
++ dword &= ~0x3f3f3f3f;
++ dword |= (delay[7] & 0x3f) << 24;
++ dword |= (delay[6] & 0x3f) << 16;
++ dword |= (delay[5] & 0x3f) << 8;
++ dword |= delay[4] & 0x3f;
++ Set_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8), dword);
++
++ /* Lane 8 (ECC) */
++ dword = Get_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8));
++ dword &= ~0x0000003f;
++ dword |= delay[8] & 0x3f;
++ Set_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8), dword);
++}
++
++/* DQS Position Training
++ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.3
++ */
+ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start)
++ struct DCTStatStruc *pDCTstat)
+ {
+ u32 Errors;
+- u8 Channel, DQSWrDelay;
++ u8 Channel;
++ u8 Receiver;
+ u8 _DisableDramECC = 0;
+- u32 PatternBuffer[292];
++ u32 PatternBuffer[304]; /* 288 + 16 */
+ u8 _Wrap32Dis = 0, _SSE2 = 0;
+- u8 dqsWrDelay_end;
+
++ u32 dev;
+ u32 addr;
++ u8 valid;
+ u32 cr4;
+ u32 lo, hi;
++ u32 index_reg;
++ uint32_t TestAddr;
++
++ uint8_t dual_rank;
++ uint8_t iter;
++ uint8_t lane;
++ uint16_t bytelane_test_results;
++ uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
++ uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
++ uint16_t write_dqs_delay_stepping_done[MAX_BYTE_LANES];
++ uint8_t dqs_read_results_array[2][MAX_BYTE_LANES][64]; /* [rank][lane][step] */
++ uint8_t dqs_write_results_array[2][MAX_BYTE_LANES][128]; /* [rank][lane][step] */
++
++ uint8_t last_pos = 0;
++ uint8_t cur_count = 0;
++ uint8_t best_pos = 0;
++ uint8_t best_count = 0;
+
+ print_debug_dqs("\nTrainDQSRdWrPos: Node_ID ", pDCTstat->Node_ID, 0);
+ cr4 = read_cr4();
+@@ -323,50 +384,363 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ SetupDqsPattern_D(pMCTstat, pDCTstat, PatternBuffer);
+
+ /* mct_BeforeTrainDQSRdWrPos_D */
+- dqsWrDelay_end = 0x20;
++
++ dev = pDCTstat->dev_dct;
++ pDCTstat->Direction = DQS_READDIR;
++
++ /* 2.8.9.9.3 (2)
++ * Loop over each channel, lane, and rank
++ */
++
++ /* NOTE
++ * The BKDG originally stated to iterate over lane, then rank, however this process is quite slow
++ * compared to an equivalent loop over rank, then lane as the latter allows multiple lanes to be
++ * tested simultaneously, thus improving performance by around 8x.
++ */
+
+ Errors = 0;
+ for (Channel = 0; Channel < 2; Channel++) {
+- print_debug_dqs("\tTrainDQSRdWrPos: 1 Channel ",Channel, 1);
++ print_debug_dqs("\tTrainDQSRdWrPos: 1 Channel ", Channel, 1);
+ pDCTstat->Channel = Channel;
+
+ if (pDCTstat->DIMMValidDCT[Channel] == 0) /* mct_BeforeTrainDQSRdWrPos_D */
+ continue;
+- pDCTstat->DqsRdWrPos_Saved = 0;
+- for ( DQSWrDelay = 0; DQSWrDelay < dqsWrDelay_end; DQSWrDelay++) {
+- pDCTstat->DQSDelay = DQSWrDelay;
+- pDCTstat->Direction = DQS_WRITEDIR;
+- mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
+-
+- print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DQSWrDelay ", DQSWrDelay, 2);
+- TrainReadDQS_D(pMCTstat, pDCTstat, cs_start);
+- print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 2);
+- if (pDCTstat->DqsRdWrPos_Saved == 0xFF)
+- break;
+-
+- print_debug_dqs("\t\tTrainDQSRdWrPos: 22 TrainErrors ",pDCTstat->TrainErrors, 2);
+- if (pDCTstat->TrainErrors == 0) {
++
++ index_reg = 0x98 + 0x100 * Channel;
++
++ dual_rank = 0;
++ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each rank of each DIMM.
++ */
++ for (; Receiver < 8; Receiver++) {
++ if ((Receiver & 0x1) == 0) {
++ /* Even rank of DIMM */
++ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1))
++ dual_rank = 1;
++ else
++ dual_rank = 0;
++ }
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
++ continue;
++ }
++
++ /* Select the base test address for the current rank */
++ TestAddr = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, Channel, Receiver, &valid);
++ if (!valid) { /* Address not supported on current CS */
++ continue;
++ }
++
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 14 TestAddr ", TestAddr, 4);
++ SetUpperFSbase(TestAddr); /* fs:eax=far ptr to target */
++
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 12 Receiver ", Receiver, 2);
++
++ /* 2.8.9.9.3 (DRAM Write Data Timing Loop)
++ * Iterate over all possible DQS delay values (0x0 - 0x7f)
++ */
++ uint8_t test_write_dqs_delay = 0;
++ uint8_t test_read_dqs_delay = 0;
++ uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
++
++ /* Initialize variables */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ current_write_dqs_delay[lane] = 0;
++ passing_dqs_delay_found[lane] = 0;
++ write_dqs_delay_stepping_done[lane] = 0;
++ }
++
++ for (test_write_dqs_delay = 0; test_write_dqs_delay < 128; test_write_dqs_delay++) {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 16 test_write_dqs_delay ", test_write_dqs_delay, 6);
++
++ /* Break out of loop if passing window already found, */
++ if (write_dqs_delay_stepping_done[0] && write_dqs_delay_stepping_done[1]
++ && write_dqs_delay_stepping_done[2] && write_dqs_delay_stepping_done[3]
++ && write_dqs_delay_stepping_done[4] && write_dqs_delay_stepping_done[5]
++ && write_dqs_delay_stepping_done[6] && write_dqs_delay_stepping_done[7])
+ break;
++
++ /* Commit the current Write Data Timing settings to the hardware registers */
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Write the DRAM training pattern to the base test address */
++ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
++
++ /* 2.8.9.9.3 (DRAM Read DQS Timing Control Loop)
++ * Iterate over all possible DQS delay values (0x0 - 0x3f)
++ */
++ for (test_read_dqs_delay = 0; test_read_dqs_delay < 64; test_read_dqs_delay++) {
++ print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 test_read_dqs_delay ", test_read_dqs_delay, 6);
++
++ /* Initialize Read DQS Timing Control settings for this iteration */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++)
++ if (!write_dqs_delay_stepping_done[lane])
++ current_read_dqs_delay[lane] = test_read_dqs_delay;
++
++ /* Commit the current Read DQS Timing Control settings to the hardware registers */
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Initialize test result variable */
++ bytelane_test_results = 0xff;
++
++ /* Read the DRAM training pattern from the base test address three times
++ * NOTE
++ * While the BKDG states to read three times this is probably excessive!
++ * Decrease training time by only reading the test pattern once per iteration
++ */
++ for (iter = 0; iter < 1; iter++) {
++ /* Flush caches */
++ SetTargetWTIO_D(TestAddr);
++ FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
++ ResetTargetWTIO_D();
++
++ /* Read and compare pattern */
++ bytelane_test_results &= (CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8) & 0xff); /* [Lane 7 :: Lane 0] 0=fail, 1=pass */
++
++ /* If all lanes have already failed testing bypass remaining re-read attempt(s) */
++ if (bytelane_test_results == 0x0)
++ break;
++ }
++
++ /* Store any lanes that passed testing for later use */
++ for (lane = 0; lane < 8; lane++)
++ if (!write_dqs_delay_stepping_done[lane])
++ dqs_read_results_array[Receiver & 0x1][lane][test_read_dqs_delay] = (!!(bytelane_test_results & (1 << lane)));
++
++ print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 162 bytelane_test_results ", bytelane_test_results, 6);
++ }
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ if (write_dqs_delay_stepping_done[lane])
++ continue;
++
++ /* Determine location and length of longest consecutive string of passing values
++ * Output is stored in best_pos and best_count
++ */
++ last_pos = 0;
++ cur_count = 0;
++ best_pos = 0;
++ best_count = 0;
++ for (iter = 0; iter < 64; iter++) {
++ if ((dqs_read_results_array[Receiver & 0x1][lane][iter]) && (iter < 63)) {
++ /* Pass */
++ cur_count++;
++ } else {
++ /* Failure or end of loop */
++ if (cur_count > best_count) {
++ best_count = cur_count;
++ best_pos = last_pos;
++ }
++ cur_count = 0;
++ last_pos = iter;
++ }
++ }
++
++ if (best_count > 2) {
++ /* Exit the DRAM Write Data Timing Loop after programming the Read DQS Timing Control
++ * register with the center of the passing window
++ */
++ current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
++ passing_dqs_delay_found[lane] = 1;
++
++ /* Commit the current Read DQS Timing Control settings to the hardware registers */
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Exit the DRAM Write Data Timing Loop */
++ write_dqs_delay_stepping_done[lane] = 1;
++
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest passing region ", best_count, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest passing region start ", best_pos, 4);
++ }
++
++ /* Increment the DQS Write Delay value if needed for the next DRAM Write Data Timing Loop iteration */
++ if (!write_dqs_delay_stepping_done[lane])
++ current_write_dqs_delay[lane]++;
++ }
+ }
+- Errors |= pDCTstat->TrainErrors;
+- }
+
+- pDCTstat->DqsRdWrPos_Saved = 0;
+- if (DQSWrDelay < dqsWrDelay_end) {
+- Errors = 0;
++ /* Flag failure(s) if present */
++ for (lane = 0; lane < 8; lane++) {
++ if (!passing_dqs_delay_found[lane]) {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 121 Unable to find passing region for lane ", lane, 2);
++
++ /* Flag absence of passing window */
++ Errors |= 1 << SB_NODQSPOS;
++ }
++ }
++
++ /* Iterate over all possible Write Data Timing values (0x0 - 0x7f)
++ * Note that the Read DQS Timing Control was calibrated / centered in the prior nested loop
++ */
++ for (test_write_dqs_delay = 0; test_write_dqs_delay < 128; test_write_dqs_delay++) {
++ /* Initialize Write Data Timing settings for this iteration */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++)
++ current_write_dqs_delay[lane] = test_write_dqs_delay;
++
++ /* Commit the current Write Data Timing settings to the hardware registers */
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Write the DRAM training pattern to the base test address */
++ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
++
++ /* Flush caches */
++ SetTargetWTIO_D(TestAddr);
++ FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
++ ResetTargetWTIO_D();
++
++ /* Read and compare pattern from the base test address */
++ bytelane_test_results = (CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8) & 0xff); /* [Lane 7 :: Lane 0] 0=fail, 1=pass */
++
++ /* Store any lanes that passed testing for later use */
++ for (lane = 0; lane < 8; lane++)
++ dqs_write_results_array[Receiver & 0x1][lane][test_write_dqs_delay] = (!!(bytelane_test_results & (1 << lane)));
++ }
++
++ for (lane = 0; lane < 8; lane++) {
++ if ((!dual_rank) || (dual_rank && (Receiver & 0x1))) {
++
++#ifdef PRINT_PASS_FAIL_BITMAPS
++ for (iter = 0; iter < 64; iter++) {
++ if (dqs_read_results_array[0][lane][iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n");
++ for (iter = 0; iter < 64; iter++) {
++ if (dqs_read_results_array[1][lane][iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n\n");
++ for (iter = 0; iter < 128; iter++) {
++ if (dqs_write_results_array[0][lane][iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n");
++ for (iter = 0; iter < 128; iter++) {
++ if (dqs_write_results_array[1][lane][iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n\n");
++#endif
++
++ /* Base rank of single-rank DIMM, or odd rank of dual-rank DIMM */
++ if (dual_rank) {
++ /* Intersect the passing windows of both ranks */
++ for (iter = 0; iter < 64; iter++)
++ if (!dqs_read_results_array[1][lane][iter])
++ dqs_read_results_array[0][lane][iter] = 0;
++ for (iter = 0; iter < 128; iter++)
++ if (!dqs_write_results_array[1][lane][iter])
++ dqs_write_results_array[0][lane][iter] = 0;
++ }
++
++ /* Determine location and length of longest consecutive string of passing values for read DQS timing
++ * Output is stored in best_pos and best_count
++ */
++ last_pos = 0;
++ cur_count = 0;
++ best_pos = 0;
++ best_count = 0;
++ for (iter = 0; iter < 64; iter++) {
++ if ((dqs_read_results_array[0][lane][iter]) && (iter < 63)) {
++ /* Pass */
++ cur_count++;
++ } else {
++ /* Failure or end of loop */
++ if (cur_count > best_count) {
++ best_count = cur_count;
++ best_pos = last_pos;
++ }
++ cur_count = 0;
++ last_pos = iter;
++ }
++ }
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest read passing region ", best_count, 4);
++ if (best_count > 0) {
++ if (best_count < MIN_DQS_WNDW) {
++ /* Flag excessively small passing window */
++ Errors |= 1 << SB_SMALLDQS;
++ }
++
++ /* Find the center of the passing window */
++ current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
++
++ /* Commit the current Read DQS Timing Control settings to the hardware registers */
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Save the final Read DQS Timing Control settings for later use */
++ pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
++ } else {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 122 Unable to find read passing region for lane ", lane, 2);
++
++ /* Flag absence of passing window */
++ Errors |= 1 << SB_NODQSPOS;
++ }
++
++ /* Determine location and length of longest consecutive string of passing values for write DQS timing
++ * Output is stored in best_pos and best_count
++ */
++ last_pos = 0;
++ cur_count = 0;
++ best_pos = 0;
++ best_count = 0;
++ for (iter = 0; iter < 128; iter++) {
++ if ((dqs_write_results_array[0][lane][iter]) && (iter < 127)) {
++ /* Pass */
++ cur_count++;
++ } else {
++ /* Failure or end of loop */
++ if (cur_count > best_count) {
++ best_count = cur_count;
++ best_pos = last_pos;
++ }
++ cur_count = 0;
++ last_pos = iter;
++ }
++ }
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region ", best_count, 4);
++ if (best_count > 0) {
++ if (best_count < MIN_DQS_WNDW) {
++ /* Flag excessively small passing window */
++ Errors |= 1 << SB_SMALLDQS;
++ }
++
++ /* Find the center of the passing window */
++ current_write_dqs_delay[lane] = (best_pos + (best_count / 2));
++
++ /* Commit the current Write Data Timing settings to the hardware registers */
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++
++ /* Save the final Write Data Timing settings for later use */
++ pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
++ } else {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 123 Unable to find write passing region for lane ", lane, 2);
++
++ /* Flag absence of passing window */
++ Errors |= 1 << SB_NODQSPOS;
++ }
++ }
++ }
+
+- print_debug_dqs("\tTrainDQSRdWrPos: 231 DQSWrDelay ", DQSWrDelay, 1);
+- TrainWriteDQS_D(pMCTstat, pDCTstat, cs_start);
+ }
+- print_debug_dqs("\tTrainDQSRdWrPos: 232 Errors ", Errors, 1);
+- pDCTstat->ErrStatus |= Errors;
+ }
+
++ pDCTstat->TrainErrors |= Errors;
++ pDCTstat->ErrStatus |= Errors;
++
+ #if DQS_TRAIN_DEBUG > 0
+ {
+ u8 val;
+ u8 i;
+- u8 Channel, Receiver, Dir;
++ u8 ChannelDTD, ReceiverDTD, Dir;
+ u8 *p;
+
+ for (Dir = 0; Dir < 2; Dir++) {
+@@ -375,14 +749,14 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
+ }
+- for (Channel = 0; Channel < 2; Channel++) {
+- printk(BIOS_DEBUG, "Channel: %02x\n", Channel);
+- for (Receiver = cs_start; Receiver < (cs_start + 2); Receiver += 2) {
+- printk(BIOS_DEBUG, "\t\tReceiver: %02x: ", Receiver);
+- p = pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][Dir];
++ for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
++ for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
++ printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
++ p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
+ for (i=0;i<8; i++) {
+ val = p[i];
+- printk(BIOS_DEBUG, "%02x ", val);
++ printk(BIOS_DEBUG, " %02x", val);
+ }
+ printk(BIOS_DEBUG, "\n");
+ }
+@@ -437,225 +811,6 @@ static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->PtrPatternBufA = (u32)buf;
+ }
+
+-static void TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start)
+-{
+- u32 Errors;
+- u8 ChipSel, DQSDelay;
+- u8 RnkDlySeqPassMin=0, RnkDlySeqPassMax=0xFF, RnkDlyFilterMin=0, RnkDlyFilterMax=0xFF;
+- u8 RnkDlySeqPassMinTot=0, RnkDlySeqPassMaxTot=0xFF, RnkDlyFilterMinTot=0, RnkDlyFilterMaxTot=0xFF;
+- u8 LastTest ,LastTestTot;
+- u32 TestAddr;
+- u8 ByteLane;
+- u8 MutualCSPassW[128];
+- u8 BanksPresent;
+- u8 dqsDelay_end;
+- u8 tmp, valid, tmp1;
+- u16 word;
+-
+- /* MutualCSPassW: each byte represents a bitmap of pass/fail per
+- * ByteLane. The indext within MutualCSPassW is the delay value
+- * given the results.
+- */
+- print_debug_dqs("\t\t\tTrainDQSPos begin ", 0, 3);
+-
+- Errors = 0;
+- BanksPresent = 0;
+-
+- dqsDelay_end = 32;
+- /* Bitmapped status per delay setting, 0xff=All positions
+- * passing (1= PASS). Set the entire array.
+- */
+- for (DQSDelay=0; DQSDelay<128; DQSDelay++) {
+- MutualCSPassW[DQSDelay] = 0xFF;
+- }
+-
+- for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) { /* logical register chipselects 0..7 */
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 11 ChipSel ", ChipSel, 4);
+-
+- if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
+- print_debug_dqs("\t\t\t\tmct_RcvrRankEnabled_D CS not enabled ", ChipSel, 4);
+- continue;
+- }
+-
+- BanksPresent = 1; /* flag for at least one bank is present */
+- TestAddr = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel, &valid);
+- if (!valid) {
+- print_debug_dqs("\t\t\t\tAddress not supported on current CS ", TestAddr, 4);
+- continue;
+- }
+-
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 12 TestAddr ", TestAddr, 4);
+- SetUpperFSbase(TestAddr); /* fs:eax=far ptr to target */
+-
+- if (pDCTstat->Direction == DQS_READDIR) {
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 13 for read ", 0, 4);
+- WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
+- }
+-
+- for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 141 DQSDelay ", DQSDelay, 5);
+-
+- tmp = 0xFF;
+- tmp1 = DQSDelay;
+- if (pDCTstat->Direction == DQS_READDIR) {
+- tmp &= MutualCSPassW[DQSDelay];
+- tmp1 += dqsDelay_end;
+- }
+- tmp &= MutualCSPassW[tmp1];
+-
+- if (tmp == 0) {
+- continue;/* skip current delay value if other chipselects have failed all 8 bytelanes */
+- }
+-
+- pDCTstat->DQSDelay = DQSDelay;
+- mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 142 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+-
+- if (pDCTstat->Direction == DQS_WRITEDIR) {
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 143 for write", 0, 5);
+- WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
+- }
+-
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 Pattern ", pDCTstat->Pattern, 5);
+- ReadDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
+- /* print_debug_dqs("\t\t\t\t\tTrainDQSPos: 145 MutualCSPassW ", MutualCSPassW[DQSDelay], 5); */
+- word = CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8); /* 0=fail, 1=pass */
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 1 ", word, 3);
+-
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 3);
+- word &= ~(pDCTstat->DqsRdWrPos_Saved); /* mask out bytelanes that already passed */
+- word &= ~(pDCTstat->DqsRdWrPos_Saved << 8);
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 2 ", word, 3);
+-
+- tmp = DQSDelay;
+- if (pDCTstat->Direction == DQS_READDIR) {
+- MutualCSPassW[tmp] &= word >> 8;
+- tmp += dqsDelay_end;
+- }
+- MutualCSPassW[tmp] &= word & 0xFF;
+-
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 146 \tMutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+-
+- SetTargetWTIO_D(TestAddr);
+- FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
+- ResetTargetWTIO_D();
+- }
+-
+- }
+-
+- if (pDCTstat->Direction == DQS_READDIR) {
+- dqsDelay_end <<= 1;
+- }
+-
+- if (BanksPresent) {
+- #if 0 /* show the bitmap */
+- for (ByteLane = 0; ByteLane < 8; ByteLane++) { /* just print ByteLane 0 */
+- for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
+- if (!(MutualCSPassW[DQSDelay] &(1 << ByteLane))) {
+- printk(BIOS_DEBUG, ".");
+- } else {
+- printk(BIOS_DEBUG, "*");
+- }
+- }
+- printk(BIOS_DEBUG, "\n");
+- }
+- #endif
+- for (ByteLane = 0; ByteLane < 8; ByteLane++) {
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 31 ByteLane ",ByteLane, 4);
+- if (!(pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane))) {
+- pDCTstat->ByteLane = ByteLane;
+- LastTest = DQS_FAIL; /* Analyze the results */
+- LastTestTot = DQS_FAIL;
+- /* RnkDlySeqPassMin = 0; */
+- /* RnkDlySeqPassMax = 0; */
+- RnkDlyFilterMax = 0;
+- RnkDlyFilterMin = 0;
+- RnkDlyFilterMaxTot = 0;
+- RnkDlyFilterMinTot = 0;
+- for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
+- if (MutualCSPassW[DQSDelay] & (1 << ByteLane)) {
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 321 DQSDelay ", DQSDelay, 5);
+- print_debug_dqs("\t\t\t\t\tTrainDQSPos: 322 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+- if (pDCTstat->Direction == DQS_READDIR)
+- tmp = 0x20;
+- else
+- tmp = 0;
+- if (DQSDelay >= tmp) {
+- RnkDlySeqPassMax = DQSDelay;
+- if (LastTest == DQS_FAIL) {
+- RnkDlySeqPassMin = DQSDelay; /* start sequential run */
+- }
+- if ((RnkDlySeqPassMax - RnkDlySeqPassMin)>(RnkDlyFilterMax-RnkDlyFilterMin)){
+- RnkDlyFilterMin = RnkDlySeqPassMin;
+- RnkDlyFilterMax = RnkDlySeqPassMax;
+- }
+- LastTest = DQS_PASS;
+- }
+-
+- if (pDCTstat->Direction == DQS_READDIR) {
+- RnkDlySeqPassMaxTot = DQSDelay;
+- if (LastTestTot == DQS_FAIL)
+- RnkDlySeqPassMinTot = DQSDelay;
+- if ((RnkDlySeqPassMaxTot - RnkDlySeqPassMinTot)>(RnkDlyFilterMaxTot-RnkDlyFilterMinTot)){
+- RnkDlyFilterMinTot = RnkDlySeqPassMinTot;
+- RnkDlyFilterMaxTot = RnkDlySeqPassMaxTot;
+- }
+- LastTestTot = DQS_PASS;
+- }
+- } else {
+- LastTest = DQS_FAIL;
+- LastTestTot = DQS_FAIL;
+- }
+- }
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 33 RnkDlySeqPassMax ", RnkDlySeqPassMax, 4);
+- if (RnkDlySeqPassMax == 0) {
+- Errors |= 1 << SB_NODQSPOS; /* no passing window */
+- } else {
+- print_debug_dqs_pair("\t\t\t\tTrainDQSPos: 34 RnkDlyFilter: ", RnkDlyFilterMin, " ", RnkDlyFilterMax, 4);
+- if (((RnkDlyFilterMax - RnkDlyFilterMin) < MIN_DQS_WNDW)){
+- Errors |= 1 << SB_SMALLDQS;
+- } else {
+- u8 middle_dqs;
+- /* mctEngDQSwindow_Save_D Not required for arrays */
+- if (pDCTstat->Direction == DQS_READDIR)
+- middle_dqs = MiddleDQS_D(RnkDlyFilterMinTot, RnkDlyFilterMaxTot);
+- else
+- middle_dqs = MiddleDQS_D(RnkDlyFilterMin, RnkDlyFilterMax);
+- pDCTstat->DQSDelay = middle_dqs;
+- mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, cs_start); /* load the register with the value */
+- if (pDCTstat->Direction == DQS_READDIR)
+- StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMinTot, RnkDlyFilterMaxTot); /* store the value into the data structure */
+- else
+- StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMin, RnkDlyFilterMax); /* store the value into the data structure */
+- print_debug_dqs("\t\t\t\tTrainDQSPos: 42 middle_dqs : ",middle_dqs, 4);
+- pDCTstat->DqsRdWrPos_Saved |= 1 << ByteLane;
+- }
+- }
+- }
+- } /* if (pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane)) */
+- }
+-/* skipLocMiddle: */
+- pDCTstat->TrainErrors = Errors;
+-
+- print_debug_dqs("\t\t\tTrainDQSPos: Errors ", Errors, 3);
+-}
+-
+-static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 ChipSel,
+- u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
+-{
+- pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
+- [pDCTstat->Direction]
+- [0]
+- [pDCTstat->ByteLane] = RnkDlyFilterMin;
+- pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
+- [pDCTstat->Direction]
+- [1]
+- [pDCTstat->ByteLane] = RnkDlyFilterMax;
+-}
+-
+ static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+ {
+@@ -679,26 +834,6 @@ static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DQSDelay;
+ }
+
+-static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 ChipSel,
+- u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
+-{
+- u8 dn;
+-
+- if (pDCTstat->Direction == DQS_WRITEDIR) {
+- dn = ChipSel >> 1;
+- RnkDlyFilterMin += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+- RnkDlyFilterMax += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+- pDCTstat->DQSDelay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+- } else {
+- RnkDlyFilterMin <<= 1;
+- RnkDlyFilterMax <<= 1;
+- pDCTstat->DQSDelay <<= 1;
+- }
+- mctEngDQSwindow_Save_D(pMCTstat, pDCTstat, ChipSel, RnkDlyFilterMin, RnkDlyFilterMax);
+- StoreDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+-}
+-
+ static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+ {
+@@ -720,33 +855,6 @@ static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+
+ /* FindDQSDatDimmVal_D is not required since we use an array */
+
+-static u8 MiddleDQS_D(u8 min, u8 max)
+-{
+- u8 size;
+- size = max-min;
+- if (size % 2)
+- size++; /* round up if the size isn't even. */
+- return ( min + (size >> 1));
+-}
+-
+-static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start)
+-{
+- print_debug_dqs("\t\tTrainReadPos ", 0, 2);
+- pDCTstat->Direction = DQS_READDIR;
+- TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
+-}
+-
+-static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start)
+-{
+- pDCTstat->Direction = DQS_WRITEDIR;
+- print_debug_dqs("\t\tTrainWritePos", 0, 2);
+- TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
+-}
+-
+ static void proc_IOCLFLUSH_D(u32 addr_hi)
+ {
+ SetTargetWTIO_D(addr_hi);
+@@ -963,30 +1071,6 @@ static void ResetTargetWTIO_D(void)
+ _WRMSR(0xc0010017, lo, hi); /* IORR0 Mask */
+ }
+
+-static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u32 TestAddr_lo)
+-{
+- /* Read a pattern of 72 bit times (per DQ), to test dram functionality.
+- * The pattern is a stress pattern which exercises both ISI and
+- * crosstalk. The number of cache lines to fill is dependent on DCT
+- * width mode and burstlength.
+- * Mode BL Lines Pattern no.
+- * ----+---+-------------------
+- * 64 4 9 0
+- * 64 8 9 0
+- * 64M 4 9 0
+- * 64M 8 9 0
+- * 128 4 18 1
+- * 128 8 N/A -
+- */
+- if (pDCTstat->Pattern == 0)
+- ReadL9TestPattern(TestAddr_lo);
+- else
+- ReadL18TestPattern(TestAddr_lo);
+- _MFENCE;
+-}
+-
+ u32 SetUpperFSbase(u32 addr_hi)
+ {
+ /* Set the upper 32-bits of the Base address, 4GB aligned) for the
+@@ -1009,8 +1093,6 @@ void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index)
+ Set_NB32_index_wait(dev, index_reg, index, val);
+ }
+
+-/* mctEngDQSwindow_Save_D not required with arrays */
+-
+ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1021,8 +1103,8 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->DCTSysLimit) {
++ TrainDQSRdWrPos_D(pMCTstat, pDCTstat);
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+- TrainDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
+ SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
+ }
+ }
+@@ -1137,27 +1219,6 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+-/*
+- * mct_SetDQSDelayAllCSR_D:
+- * Write the Delay value to all eight byte lanes.
+- */
+-static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u8 cs_start)
+-{
+- u8 ByteLane;
+- u8 ChipSel = cs_start;
+-
+- for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) {
+- if ( mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
+- for (ByteLane = 0; ByteLane < 8; ByteLane++) {
+- pDCTstat->ByteLane = ByteLane;
+- mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, ChipSel);
+- }
+- }
+- }
+-}
+-
+ u8 mct_RcvrRankEnabled_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 ChipSel)
+@@ -1196,7 +1257,7 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+ reg = 0x40 + (receiver << 2) + reg_off;
+ val = Get_NB32(dev, reg);
+
+- val &= ~0x0F;
++ val &= ~0xe007c01f;
+
+ /* unganged mode DCT0+DCT1, sys addr of DCT1=node
+ * base+DctSelBaseAddr+local ca base*/
+@@ -1277,6 +1338,7 @@ exitGetAddrWNoError:
+ print_debug_dqs("mct_GetMCTSysAddr_D: base_addr ", val, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: valid ", *valid, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: status ", pDCTstat->Status, 2);
++ print_debug_dqs("mct_GetMCTSysAddr_D: SysBase ", pDCTstat->DCTSysBase, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: HoleBase ", pDCTstat->DCTHoleBase, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: Cachetop ", pMCTstat->Sub4GCacheTop, 2);
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+index 528c782..60bc01d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -25,7 +26,6 @@ static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStr
+ static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+-static void MultiplyDelay(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+ static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+@@ -154,7 +154,6 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+- MultiplyDelay(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1)))
+ AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
+@@ -162,6 +161,9 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++/* Write Levelization Training
++ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1
++ */
+ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+index 3d625de..596fb23 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -201,12 +202,13 @@ static void SetMTRRrange_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr, u16 MtrrType)
+
+ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ {
+-/* UMA memory size may need splitting the MTRR configuration into two
+- Before training use NB_BottomIO or the physical memory size to set the MTRRs.
+- After training, add UMAMemTyping function to reconfigure the MTRRs based on
+- NV_BottomUMA (for UMA systems only).
+- This two-step process allows all memory to be cached for training
+-*/
++ /* UMA memory size may need splitting the MTRR configuration into two
++ * Before training use NB_BottomIO or the physical memory size to set the MTRRs.
++ * After training, add UMAMemTyping function to reconfigure the MTRRs based on
++ * NV_BottomUMA (for UMA systems only).
++ * This two-step process allows all memory to be cached for training
++ */
++
+ u32 Bottom32bIO, Cache32bTOP;
+ u32 val;
+ u32 addr;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c
+index 013a1b9..6f97061 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -140,7 +141,7 @@ void InterleaveNodes_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ if (DoIntlv) {
+- MCTMemClr_D(pMCTstat,pDCTstatA);
++ MCTMemClr_D(pMCTstat, pDCTstatA);
+ /* Program Interleaving enabled on Node 0 map only.*/
+ MemSize0 <<= bsf(Nodes); /* MemSize=MemSize*2 (or 4, or 8) */
+ Dct0MemSize <<= bsf(Nodes);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index da2f372..cda9c6b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -36,10 +37,10 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+ val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78);
+
+ val &= 7;
+- val = ((~val) & 0xFF) + 1;
++ val = ((~val) & 0xff) + 1;
+ val += 6;
+- val &= 0xFF;
+- misc2 &= 0xFFF8FFFF;
++ val &= 0x7;
++ misc2 &= 0xfff8ffff;
+ misc2 |= val << 16; /* DataTxFifoWrDly */
+ if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
+ misc2 |= 1 << 7; /* ProgOdtEn */
+@@ -52,11 +53,15 @@ void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat)
+ u32 val;
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx)) {
+- Set_NB32(pDCTstat->dev_dct, 0x11C, 0x0CE00FC0 | 1 << 29/* FlushWrOnStpGnt */);
++ /* Revision C */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, 0x0ce00fc0 | 1 << 29/* FlushWrOnStpGnt */);
++ }
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x1B0);
+- val &= 0xFFFFF8C0;
++ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx)) {
++ val = Get_NB32(pDCTstat->dev_dct, 0x1b0);
++ val &= ~0x73f;
+ val |= 0x101; /* BKDG recommended settings */
+- Set_NB32(pDCTstat->dev_dct, 0x1B0, val);
++
++ Set_NB32(pDCTstat->dev_dct, 0x1b0, val);
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 6de2f4e..b21b96a 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -172,6 +173,7 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ ret |= 1 << 11;
+ }
+
++ /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
+ if (dword & (1 << 13))
+ ret |= 1 << 12;
+
+@@ -199,7 +201,8 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
+ /* program MrsAddress[6:4,2]=read CAS latency
+ (CL):based on F2x[1,0]88[Tcl] */
+ dword2 = Get_NB32(dev, reg_off + 0x88);
+- ret |= (dword2 & 0xF) << 4; /* F2x88[3:0] to MrsAddress[6:4,2]=xxx0b */
++ ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */
++ ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */
+
+ /* program MrsAddress[12]=0 (PPD):slow exit */
+ if (dword & (1 << 23))
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 8e5c268..587c414 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,25 +25,13 @@
+
+ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass);
+-static u8 mct_SavePassRcvEnDly_D(struct DCTStatStruc *pDCTstat,
+- u8 rcvrEnDly, u8 Channel,
+- u8 receiver, u8 Pass);
+-static u8 mct_CompareTestPatternQW0_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u32 addr, u8 channel,
+- u8 pattern, u8 Pass);
+ static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel);
+ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel);
+-static void mct_SetFinalRcvrEnDly_D(struct DCTStatStruc *pDCTstat,
+- u8 RcvrEnDly, u8 where,
+- u8 Channel, u8 Receiver,
+- u32 dev, u32 index_reg,
+- u8 Addl_Index, u8 Pass);
+-static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQSRcvEnDly);
++static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 DQSRcvEnDly);
+ static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat);
+@@ -50,17 +39,17 @@ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat);
+ /* Warning: These must be located so they do not cross a logical 16-bit
+ segment boundary! */
+ static const u32 TestPattern0_D[] = {
+- 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+- 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+- 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+- 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+-};
+-static const u32 TestPattern1_D[] = {
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ };
++static const u32 TestPattern1_D[] = {
++ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
++ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
++ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
++ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
++};
+ static const u32 TestPattern2_D[] = {
+ 0x12345678, 0x87654321, 0x23456789, 0x98765432,
+ 0x59385824, 0x30496724, 0x24490795, 0x99938733,
+@@ -104,16 +93,87 @@ void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ dqsTrainRcvrEn_SW(pMCTstat, pDCTstat, Pass);
+ }
+
++static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t wdt_reg;
++ if ((lane == 0) || (lane == 1))
++ wdt_reg = 0x30;
++ if ((lane == 2) || (lane == 3))
++ wdt_reg = 0x31;
++ if ((lane == 4) || (lane == 5))
++ wdt_reg = 0x40;
++ if ((lane == 6) || (lane == 7))
++ wdt_reg = 0x41;
++ if (lane == 8)
++ wdt_reg = 0x32;
++ wdt_reg += dimm * 3;
++ dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1))
++ current_total_delay[lane] = (dword & 0x00ff0000) >> 16;
++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0))
++ current_total_delay[lane] = dword & 0x000000ff;
++ }
++}
++
++static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < 8; lane++) {
++ uint32_t ret_reg;
++ if ((lane == 0) || (lane == 1))
++ ret_reg = 0x10;
++ if ((lane == 2) || (lane == 3))
++ ret_reg = 0x11;
++ if ((lane == 4) || (lane == 5))
++ ret_reg = 0x20;
++ if ((lane == 6) || (lane == 7))
++ ret_reg = 0x21;
++ ret_reg += dimm * 3;
++ dword = Get_NB32_index_wait(dev, index_reg, ret_reg);
++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
++ dword &= ~(0x1ff << 16);
++ dword |= (current_total_delay[lane] & 0x1ff) << 16;
++ }
++ if ((lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
++ dword &= ~0x1ff;
++ dword |= current_total_delay[lane] & 0x1ff;
++ }
++ Set_NB32_index_wait(dev, index_reg, ret_reg, dword);
++ }
++}
++
++static uint32_t convert_testaddr_and_channel_to_address(struct DCTStatStruc *pDCTstat, uint32_t testaddr, uint8_t channel)
++{
++ SetUpperFSbase(testaddr);
++ testaddr <<= 8;
++
++ if((pDCTstat->Status & (1<<SB_128bitmode)) && channel ) {
++ testaddr += 8; /* second channel */
++ }
++
++ return testaddr;
++}
++
++/* DQS Receiver Enable Training
++ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.2
++ */
+ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass)
+ {
+- u8 Channel, RcvrEnDly, RcvrEnDlyRmin;
+- u8 Test0, Test1, CurrTest, CurrTestSide0, CurrTestSide1;
+- u8 CTLRMaxDelay, _2Ranks, PatternA, PatternB;
++ u8 Channel;
++ u8 _2Ranks;
+ u8 Addl_Index = 0;
+ u8 Receiver;
+ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
+- u8 RcvrEnDlyLimit, Final_Value, MaxDelay_CH[2];
++ u8 Final_Value;
++ u16 CTLRMaxDelay;
++ u16 MaxDelay_CH[2];
+ u32 TestAddr0, TestAddr1, TestAddr0B, TestAddr1B;
+ u32 PatternBuffer[64+4]; /* FIXME: need increase 8? */
+ u32 Errors;
+@@ -127,9 +187,20 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ u32 cr4;
+ u32 lo, hi;
+
++ uint32_t dword;
++ uint8_t rank;
++ uint8_t lane;
++ uint16_t current_total_delay[MAX_BYTE_LANES];
++ uint16_t candidate_total_delay[8];
++ uint8_t data_test_pass_sr[2][8]; /* [rank][lane] */
++ uint8_t data_test_pass[8]; /* [lane] */
++ uint8_t data_test_pass_prev[8]; /* [lane] */
++ uint8_t window_det_toggle[8];
++ uint8_t trained[8];
++ uint64_t result_qword1;
++ uint64_t result_qword2;
++
+ u8 valid;
+- u32 tmp;
+- u8 LastTest;
+
+ print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
+ print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
+@@ -181,33 +252,103 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+
+ Errors = 0;
+ dev = pDCTstat->dev_dct;
+- CTLRMaxDelay = 0;
+
+ for (Channel = 0; Channel < 2; Channel++) {
+ print_debug_dqs("\tTrainRcvEn51: Node ", pDCTstat->Node_ID, 1);
+ print_debug_dqs("\tTrainRcvEn51: Channel ", Channel, 1);
+ pDCTstat->Channel = Channel;
+
++ CTLRMaxDelay = 0;
+ MaxDelay_CH[Channel] = 0;
+ index_reg = 0x98 + 0x100 * Channel;
+
+ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
+- /* There are four receiver pairs, loosely associated with chipselects. */
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each DIMM.
++ */
+ for (; Receiver < 8; Receiver += 2) {
+ Addl_Index = (Receiver >> 1) * 3 + 0x10;
+- LastTest = DQS_FAIL;
+-
+- /* mct_ModifyIndex_D */
+- RcvrEnDlyRmin = RcvrEnDlyLimit = 0xff;
+
+ print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
+
+- if(!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
+ continue;
+ }
+
++ /* Clear data structures */
++ for (lane = 0; lane < 8; lane++) {
++ data_test_pass_prev[lane] = 0;
++ trained[lane] = 0;
++ }
++
++ /* 2.8.9.9.2 (1, 6)
++ * Retrieve gross and fine timing fields from write DQS registers
++ */
++ read_dqs_write_timing_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++
++ /* 2.8.9.9.2 (1)
++ * Program the Write Data Timing and Write ECC Timing register to
++ * the values stored in the DQS Write Timing Control register
++ * for each lane
++ */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t wdt_reg;
++
++ /* Calculate Write Data Timing register location */
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ wdt_reg = 0x1;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ wdt_reg = 0x2;
++ if (lane == 8)
++ wdt_reg = 0x3;
++ wdt_reg |= ((Receiver / 2) << 8);
++
++ /* Set Write Data Timing register values */
++ dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
++ if ((lane == 7) || (lane == 3)) {
++ dword &= ~(0x7f << 24);
++ dword |= (current_total_delay[lane] & 0x7f) << 24;
++ }
++ if ((lane == 6) || (lane == 2)) {
++ dword &= ~(0x7f << 16);
++ dword |= (current_total_delay[lane] & 0x7f) << 16;
++ }
++ if ((lane == 5) || (lane == 1)) {
++ dword &= ~(0x7f << 8);
++ dword |= (current_total_delay[lane] & 0x7f) << 8;
++ }
++ if ((lane == 8) || (lane == 4) || (lane == 0)) {
++ dword &= ~0x7f;
++ dword |= current_total_delay[lane] & 0x7f;
++ }
++ Set_NB32_index_wait(dev, index_reg, wdt_reg, dword);
++ }
++
++ /* 2.8.9.9.2 (2)
++ * Program the Read DQS Timing Control and the Read DQS ECC Timing Control registers
++ * to 1/2 MEMCLK for all lanes
++ */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t rdt_reg;
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ rdt_reg = 0x5;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ rdt_reg = 0x6;
++ if (lane == 8)
++ rdt_reg = 0x7;
++ rdt_reg |= ((Receiver / 2) << 8);
++ if (lane == 8)
++ dword = 0x0000003f;
++ else
++ dword = 0x3f3f3f3f;
++ Set_NB32_index_wait(dev, index_reg, rdt_reg, dword);
++ }
++
++ /* 2.8.9.9.2 (3)
++ * Select two test addresses for each rank present
++ */
+ TestAddr0 = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, Channel, Receiver, &valid);
+- if(!valid) { /* Address not supported on current CS */
++ if (!valid) { /* Address not supported on current CS */
+ continue;
+ }
+
+@@ -229,171 +370,215 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr1 ", TestAddr1, 2);
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr1B ", TestAddr1B, 2);
+
+- /*
+- * Get starting RcvrEnDly value
++ /* 2.8.9.9.2 (4, 5)
++ * Write 1 cache line of the appropriate test pattern to each test addresse
+ */
+- RcvrEnDly = mct_Get_Start_RcvrEnDly_1Pass(Pass);
++ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, 0); /* rank 0 of DIMM, testpattern 0 */
++ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, 1); /* rank 0 of DIMM, testpattern 1 */
++ if (_2Ranks) {
++ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1, 0); /*rank 1 of DIMM, testpattern 0 */
++ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1B, 1); /*rank 1 of DIMM, testpattern 1 */
++ }
+
+- /* mct_GetInitFlag_D*/
+- if (Pass == FirstPass) {
+- pDCTstat->DqsRcvEn_Pass = 0;
+- } else {
+- pDCTstat->DqsRcvEn_Pass=0xFF;
++#if DQS_TRAIN_DEBUG > 0
++ for (lane = 0; lane < 8; lane++) {
++ print_debug_dqs("\t\tTrainRcvEn54: lane: ", lane, 2);
++ print_debug_dqs("\t\tTrainRcvEn54: current_total_delay ", current_total_delay[lane], 2);
+ }
+- pDCTstat->DqsRcvEn_Saved = 0;
++#endif
+
++ /* 2.8.9.9.2 (6)
++ * Write gross and fine timing fields to read DQS registers
++ */
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++
++ /* 2.8.9.9.2 (7)
++ * Loop over all delay values up to 1 MEMCLK (0x40 delay steps) from the initial delay values
++ *
++ * FIXME
++ * It is not clear if training should be discontinued if any test failures occur in the first
++ * 1 MEMCLK window, or if it should be discontinued if no successes occur in the first 1 MEMCLK
++ * window. Therefore, loop over up to 2 MEMCLK (0x80 delay steps) to be on the safe side.
++ */
++ uint16_t current_delay_step;
+
+- while(RcvrEnDly < RcvrEnDlyLimit) { /* sweep Delay value here */
+- print_debug_dqs("\t\t\tTrainRcvEn541: RcvrEnDly ", RcvrEnDly, 3);
++ for (current_delay_step = 0; current_delay_step < 0x80; current_delay_step++) {
++ print_debug_dqs("\t\t\tTrainRcvEn541: current_delay_step ", current_delay_step, 3);
+
+- /* callback not required
+- if(mct_AdjustDelay_D(pDCTstat, RcvrEnDly))
+- goto skipDly;
++ /* 2.8.9.9.2 (7 D)
++ * Terminate if all lanes are trained
+ */
++ uint8_t all_lanes_trained = 1;
++ for (lane = 0; lane < 8; lane++)
++ if (!trained[lane])
++ all_lanes_trained = 0;
+
+- /* Odd steps get another pattern such that even
+- and odd steps alternate. The pointers to the
+- patterns will be swaped at the end of the loop
+- so that they correspond. */
+- if(RcvrEnDly & 1) {
+- PatternA = 1;
+- PatternB = 0;
+- } else {
+- /* Even step */
+- PatternA = 0;
+- PatternB = 1;
+- }
+-
+- mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, PatternA); /* rank 0 of DIMM, testpattern 0 */
+- mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, PatternB); /* rank 0 of DIMM, testpattern 1 */
+- if(_2Ranks) {
+- mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1, PatternA); /*rank 1 of DIMM, testpattern 0 */
+- mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1B, PatternB); /*rank 1 of DIMM, testpattern 1 */
+- }
+-
+- mct_SetRcvrEnDly_D(pDCTstat, RcvrEnDly, 0, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
+-
+- CurrTest = DQS_FAIL;
+- CurrTestSide0 = DQS_FAIL;
+- CurrTestSide1 = DQS_FAIL;
+-
+- mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0); /*cache fills */
+- Test0 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr0, Channel, PatternA, Pass);/* ROM vs cache compare */
+- proc_IOCLFLUSH_D(TestAddr0);
+- ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+-
+- print_debug_dqs("\t\t\tTrainRcvEn542: Test0 result ", Test0, 3);
+-
+- /* != 0x00 mean pass */
+-
+- if(Test0 == DQS_PASS) {
+- mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B); /*cache fills */
+- /* ROM vs cache compare */
+- Test1 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr0B, Channel, PatternB, Pass);
+- proc_IOCLFLUSH_D(TestAddr0B);
+- ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+-
+- print_debug_dqs("\t\t\tTrainRcvEn543: Test1 result ", Test1, 3);
++ if (all_lanes_trained)
++ break;
+
+- if(Test1 == DQS_PASS) {
+- CurrTestSide0 = DQS_PASS;
++ /* 2.8.9.9.2 (7 A)
++ * Loop over all ranks
++ */
++ for (rank = 0; rank < (_2Ranks + 1); rank++) {
++ /* 2.8.9.9.2 (7 A a-d)
++ * Read the first test address of the current rank
++ * Store the first data beat for analysis
++ * Reset read pointer in the DRAM controller FIFO
++ * Read the second test address of the current rank
++ * Store the first data beat for analysis
++ * Reset read pointer in the DRAM controller FIFO
++ */
++ if (rank & 1) {
++ /* 2.8.9.9.2 (7 D)
++ * Invert read instructions to alternate data read order on the bus
++ */
++ proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
++ result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
++ result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ } else {
++ proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
++ result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
++ result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+ }
+- }
+- if(_2Ranks) {
+- mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1); /*cache fills */
+- /* ROM vs cache compare */
+- Test0 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr1, Channel, PatternA, Pass);
+- proc_IOCLFLUSH_D(TestAddr1);
+- ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+-
+- print_debug_dqs("\t\t\tTrainRcvEn544: Test0 result ", Test0, 3);
+-
+- if(Test0 == DQS_PASS) {
+- mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1B); /*cache fills */
+- /* ROM vs cache compare */
+- Test1 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr1B, Channel, PatternB, Pass);
+- proc_IOCLFLUSH_D(TestAddr1B);
+- ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+-
+- print_debug_dqs("\t\t\tTrainRcvEn545: Test1 result ", Test1, 3);
+- if(Test1 == DQS_PASS) {
+- CurrTestSide1 = DQS_PASS;
++ /* 2.8.9.9.2 (7 A e)
++ * Compare both read patterns and flag passing ranks/lanes
++ */
++ uint8_t result_lane_byte1;
++ uint8_t result_lane_byte2;
++ for (lane = 0; lane < 8; lane++) {
++ if (trained[lane] == 1) {
++#if DQS_TRAIN_DEBUG > 0
++ print_debug_dqs("\t\t\t\t\t\t\t\t lane already trained: ", lane, 4);
++#endif
++ continue;
+ }
++
++ result_lane_byte1 = (result_qword1 >> (lane * 8)) & 0xff;
++ result_lane_byte2 = (result_qword2 >> (lane * 8)) & 0xff;
++ if ((result_lane_byte1 == 0x55) && (result_lane_byte2 == 0xaa))
++ data_test_pass_sr[rank][lane] = 1;
++ else
++ data_test_pass_sr[rank][lane] = 0;
++#if DQS_TRAIN_DEBUG > 0
++ print_debug_dqs_pair("\t\t\t\t\t\t\t\t ", 0x55, " | ", result_lane_byte1, 4);
++ print_debug_dqs_pair("\t\t\t\t\t\t\t\t ", 0xaa, " | ", result_lane_byte2, 4);
++#endif
++
+ }
+ }
+
+- if(_2Ranks) {
+- if ((CurrTestSide0 == DQS_PASS) && (CurrTestSide1 == DQS_PASS)) {
+- CurrTest = DQS_PASS;
++ /* 2.8.9.9.2 (7 B)
++ * If DIMM is dual rank, only use delays that pass testing for both ranks
++ */
++ for (lane = 0; lane < 8; lane++) {
++ if (_2Ranks) {
++ if ((data_test_pass_sr[0][lane]) && (data_test_pass_sr[1][lane]))
++ data_test_pass[lane] = 1;
++ else
++ data_test_pass[lane] = 0;
++ } else {
++ data_test_pass[lane] = data_test_pass_sr[0][lane];
+ }
+- } else if (CurrTestSide0 == DQS_PASS) {
+- CurrTest = DQS_PASS;
+ }
+
+- /* record first pass DqsRcvEn to stack */
+- valid = mct_SavePassRcvEnDly_D(pDCTstat, RcvrEnDly, Channel, Receiver, Pass);
++ /* 2.8.9.9.2 (7 E)
++ * For each lane, update the DQS receiver delay setting in support of next iteration
++ */
++ for (lane = 0; lane < 8; lane++) {
++ if (trained[lane] == 1)
++ continue;
++
++ /* 2.8.9.9.2 (7 C a)
++ * Save the total delay of the first success after a failure for later use
++ */
++ if ((data_test_pass[lane] == 1) && (data_test_pass_prev[lane] == 0)) {
++ candidate_total_delay[lane] = current_total_delay[lane];
++ window_det_toggle[lane] = 0;
++ }
+
+- /* Break(1:RevF,2:DR) or not(0) FIXME: This comment deosn't make sense */
+- if(valid == 2 || (LastTest == DQS_FAIL && valid == 1)) {
+- RcvrEnDlyRmin = RcvrEnDly;
+- break;
++ /* 2.8.9.9.2 (7 C b)
++ * If the current delay failed testing add 1/8 UI to the current delay
++ */
++ if (data_test_pass[lane] == 0)
++ current_total_delay[lane] += 0x4;
++
++ /* 2.8.9.9.2 (7 C c)
++ * If the current delay passed testing alternately add either 1/32 UI or 1/4 UI to the current delay
++ * If 1.25 UI of delay have been added with no failures the lane is considered trained
++ */
++ if (data_test_pass[lane] == 1) {
++ /* See if lane is trained */
++ if ((current_total_delay[lane] - candidate_total_delay[lane]) >= 0x28) {
++ trained[lane] = 1;
++
++ /* Calculate and set final lane delay value
++ * The final delay is the candidate delay + 7/8 UI
++ */
++ current_total_delay[lane] = candidate_total_delay[lane] + 0x1c;
++ } else {
++ if (window_det_toggle[lane] == 0) {
++ current_total_delay[lane] += 0x1;
++ window_det_toggle[lane] = 1;
++ } else {
++ current_total_delay[lane] += 0x8;
++ window_det_toggle[lane] = 0;
++ }
++ }
++ }
+ }
+
+- LastTest = CurrTest;
+-
+- /* swap the rank 0 pointers */
+- tmp = TestAddr0;
+- TestAddr0 = TestAddr0B;
+- TestAddr0B = tmp;
+-
+- /* swap the rank 1 pointers */
+- tmp = TestAddr1;
+- TestAddr1 = TestAddr1B;
+- TestAddr1B = tmp;
+-
+- print_debug_dqs("\t\t\tTrainRcvEn56: RcvrEnDly ", RcvrEnDly, 3);
++ /* Update delays in hardware */
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+
+- RcvrEnDly++;
+-
+- } /* while RcvrEnDly */
+-
+- print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDly ", RcvrEnDly, 2);
+- print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDlyRmin ", RcvrEnDlyRmin, 3);
+- print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDlyLimit ", RcvrEnDlyLimit, 3);
+- if(RcvrEnDlyRmin == RcvrEnDlyLimit) {
+- /* no passing window */
+- pDCTstat->ErrStatus |= 1 << SB_NORCVREN;
+- Errors |= 1 << SB_NORCVREN;
+- pDCTstat->ErrCode = SC_FatalErr;
++ /* Save previous results for comparison in the next iteration */
++ for (lane = 0; lane < 8; lane++)
++ data_test_pass_prev[lane] = data_test_pass[lane];
+ }
+
+- if(RcvrEnDly > (RcvrEnDlyLimit - 1)) {
+- /* passing window too narrow, too far delayed*/
+- pDCTstat->ErrStatus |= 1 << SB_SmallRCVR;
+- Errors |= 1 << SB_SmallRCVR;
+- pDCTstat->ErrCode = SC_FatalErr;
+- RcvrEnDly = RcvrEnDlyLimit - 1;
+- pDCTstat->CSTrainFail |= 1 << Receiver;
+- pDCTstat->DimmTrainFail |= 1 << (Receiver + Channel);
+- }
+-
+- /* CHB_D0_B0_RCVRDLY set in mct_Average_RcvrEnDly_Pass */
+- mct_Average_RcvrEnDly_Pass(pDCTstat, RcvrEnDly, RcvrEnDlyLimit, Channel, Receiver, Pass);
+-
+- mct_SetFinalRcvrEnDly_D(pDCTstat, RcvrEnDly, Final_Value, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
++#if DQS_TRAIN_DEBUG > 0
++ for (lane = 0; lane < 8; lane++)
++ print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
++#endif
+
+- if(pDCTstat->ErrStatus & (1 << SB_SmallRCVR)) {
+- Errors |= 1 << SB_SmallRCVR;
+- }
++ /* Find highest delay value and save for later use */
++ for (lane = 0; lane < 8; lane++)
++ if (current_total_delay[lane] > CTLRMaxDelay)
++ CTLRMaxDelay = current_total_delay[lane];
+
+- RcvrEnDly += Pass1MemClkDly;
+- if(RcvrEnDly > CTLRMaxDelay) {
+- CTLRMaxDelay = RcvrEnDly;
++ /* See if any lanes failed training, and set error flags appropriately
++ * For all trained lanes, save delay values for later use
++ */
++ for (lane = 0; lane < 8; lane++) {
++ if (trained[lane]) {
++ pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver >> 1][lane] = current_total_delay[lane];
++ } else {
++ printk(BIOS_WARNING, "TrainRcvrEn: WARNING: Lane %d of receiver %d on channel %d failed training!\n", lane, Receiver, Channel);
++
++ /* Set error flags */
++ pDCTstat->ErrStatus |= 1 << SB_NORCVREN;
++ Errors |= 1 << SB_NORCVREN;
++ pDCTstat->ErrCode = SC_FatalErr;
++ pDCTstat->CSTrainFail |= 1 << Receiver;
++ pDCTstat->DimmTrainFail |= 1 << (Receiver + Channel);
++ }
+ }
+
+- } /* while Receiver */
++ /* 2.8.9.9.2 (8)
++ * Flush the receiver FIFO
++ * Write one full cache line of non-0x55/0xaa data to one of the test addresses, then read it back to flush the FIFO
++ */
++
++ WriteLNTestPattern(TestAddr0 << 8, (uint8_t *)TestPattern2_D, 1);
++ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0);
++ }
+ MaxDelay_CH[Channel] = CTLRMaxDelay;
+- } /* for Channel */
++ }
+
+ CTLRMaxDelay = MaxDelay_CH[0];
+ if (MaxDelay_CH[1] > CTLRMaxDelay)
+@@ -428,31 +613,31 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+
+ #if DQS_TRAIN_DEBUG > 0
+ {
+- u8 Channel;
++ u8 ChannelDTD;
+ printk(BIOS_DEBUG, "TrainRcvrEn: CH_MaxRdLat:\n");
+- for(Channel = 0; Channel<2; Channel++) {
++ for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
+ printk(BIOS_DEBUG, "Channel:%x: %x\n",
+- Channel, pDCTstat->CH_MaxRdLat[Channel]);
++ ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
+ }
+ }
+ #endif
+
+ #if DQS_TRAIN_DEBUG > 0
+ {
+- u8 val;
+- u8 Channel, Receiver;
++ u16 valDTD;
++ u8 ChannelDTD, ReceiverDTD;
+ u8 i;
+- u8 *p;
++ u16 *p;
+
+ printk(BIOS_DEBUG, "TrainRcvrEn: CH_D_B_RCVRDLY:\n");
+- for(Channel = 0; Channel < 2; Channel++) {
+- printk(BIOS_DEBUG, "Channel:%x\n", Channel);
+- for(Receiver = 0; Receiver<8; Receiver+=2) {
+- printk(BIOS_DEBUG, "\t\tReceiver:%x:", Receiver);
+- p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver>>1];
++ for(ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel:%x\n", ChannelDTD);
++ for(ReceiverDTD = 0; ReceiverDTD<8; ReceiverDTD+=2) {
++ printk(BIOS_DEBUG, "\t\tReceiver:%x:", ReceiverDTD);
++ p = pDCTstat->CH_D_B_RCVRDLY[ChannelDTD][ReceiverDTD>>1];
+ for (i=0;i<8; i++) {
+- val = p[i];
+- printk(BIOS_DEBUG, "%x ", val);
++ valDTD = p[i];
++ printk(BIOS_DEBUG, " %03x", valDTD);
+ }
+ printk(BIOS_DEBUG, "\n");
+ }
+@@ -475,15 +660,6 @@ u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct)
+ }
+ }
+
+-static void mct_SetFinalRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 where, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass/*, u8 *p*/)
+-{
+- /*
+- * Program final DqsRcvEnDly to additional index for DQS receiver
+- * enabled delay
+- */
+- mct_SetRcvrEnDly_D(pDCTstat, RcvrEnDly, where, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
+-}
+-
+ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat)
+ {
+ u8 ch_end, ch;
+@@ -514,17 +690,20 @@ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat)
+ * Function only used once so it was inlined.
+ */
+
+-void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly,
++/* Set F2x[1, 0]9C_x[2B:10] DRAM DQS Receiver Enable Timing Control Registers
++ * See BKDG Rev. 3.62 page 268 for more information
++ */
++void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly,
+ u8 FinalValue, u8 Channel, u8 Receiver, u32 dev,
+ u32 index_reg, u8 Addl_Index, u8 Pass)
+ {
+ u32 index;
+ u8 i;
+- u8 *p;
++ u16 *p;
+ u32 val;
+
+- if(RcvrEnDly == 0xFE) {
+- /*set the boudary flag */
++ if(RcvrEnDly == 0x1fe) {
++ /*set the boundary flag */
+ pDCTstat->Status |= 1 << SB_DQSRcvLimit;
+ }
+
+@@ -543,27 +722,57 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly,
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ if(i & 1) {
+ /* odd byte lane */
+- val &= ~(0xFF << 16);
+- val |= (RcvrEnDly << 16);
++ val &= ~(0x1ff << 16);
++ val |= ((RcvrEnDly & 0x1ff) << 16);
+ } else {
+ /* even byte lane */
+- val &= ~0xFF;
+- val |= RcvrEnDly;
++ val &= ~0x1ff;
++ val |= (RcvrEnDly & 0x1ff);
+ }
+ Set_NB32_index_wait(dev, index_reg, index, val);
+ }
+
+ }
+
+-static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQSRcvEnDly)
++/* Calculate MaxRdLatency
++ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.5
++ */
++static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 DQSRcvEnDly)
+ {
+ u32 dev;
+ u32 reg;
+- u16 SubTotal;
++ u32 SubTotal;
+ u32 index_reg;
+ u32 reg_off;
+ u32 val;
+- u32 valx;
++
++ uint8_t cpu_val_n;
++ uint8_t cpu_val_p;
++
++ u16 freq_tab[] = {400, 533, 667, 800};
++
++ /* Set up processor-dependent values */
++ if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
++ /* Revision D and above */
++ cpu_val_n = 4;
++ cpu_val_p = 29;
++ } else if (pDCTstat->LogicalCPUID & AMD_DR_Cx) {
++ /* Revision C */
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++ if ((package_type == PT_L1) /* Socket F (1207) */
++ || (package_type == PT_M2) /* Socket AM3 */
++ || (package_type == PT_S1)) { /* Socket S1g<x> */
++ cpu_val_n = 10;
++ cpu_val_p = 11;
++ } else {
++ cpu_val_n = 4;
++ cpu_val_p = 29;
++ }
++ } else {
++ /* Revision B and below */
++ cpu_val_n = 10;
++ cpu_val_p = 11;
++ }
+
+ if(pDCTstat->GangedMode)
+ Channel = 0;
+@@ -598,49 +807,32 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQ
+ val = Get_NB32(dev, 0x78 + reg_off);
+ SubTotal += 8 - (val & 0x0f);
+
+- /* Convert bits 7-5 (also referred to as the course delay) of
++ /* Convert bits 7-5 (also referred to as the coarse delay) of
+ * the current (or worst case) DQS receiver enable delay to
+ * 1/2 MEMCLKs units, rounding up, and add this to the sub-total.
+ */
+- SubTotal += DQSRcvEnDly >> 5; /*BOZO-no rounding up */
++ SubTotal += DQSRcvEnDly >> 5; /* Retrieve gross delay portion of value */
+
+- /* Add 5.5 to the sub-total. 5.5 represents part of the
++ /* Add "P" to the sub-total. "P" represents part of the
+ * processor specific constant delay value in the DRAM
+ * clock domain.
+ */
+ SubTotal <<= 1; /*scale 1/2 MemClk to 1/4 MemClk */
+- SubTotal += 11; /*add 5.5 1/2MemClk */
++ SubTotal += cpu_val_p; /*add "P" 1/2MemClk */
++ SubTotal >>= 1; /*scale 1/4 MemClk back to 1/2 MemClk */
+
+ /* Convert the sub-total (in 1/2 MEMCLKs) to northbridge
+- * clocks (NCLKs) as follows (assuming DDR400 and assuming
+- * that no P-state or link speed changes have occurred).
++ * clocks (NCLKs)
+ */
++ SubTotal *= 200 * ((Get_NB32(pDCTstat->dev_nbmisc, 0xd4) & 0x1f) + 4);
++ SubTotal /= freq_tab[((Get_NB32(pDCTstat->dev_dct, 0x94 + reg_off) & 0x7) - 3)];
++ SubTotal = (SubTotal + (2 - 1)) / 2; /* Round up */
+
+- /* New formula:
+- * SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */
+- val = Get_NB32(dev, 0x94 + reg_off);
+-
+- /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
+- val &= 7;
+- if (val >= 3) {
+- val <<= 1;
+- } else
+- val += 3;
+- valx = val << 2;
+-
+- val = Get_NB32(pDCTstat->dev_nbmisc, 0xD4);
+- SubTotal *= ((val & 0x1f) + 4 ) * 3;
+-
+- SubTotal /= valx;
+- if (SubTotal % valx) { /* round up */
+- SubTotal++;
+- }
+-
+- /* Add 5 NCLKs to the sub-total. 5 represents part of the
++ /* Add "N" NCLKs to the sub-total. "N" represents part of the
+ * processor specific constant value in the northbridge
+ * clock domain.
+ */
+- SubTotal += 5;
++ SubTotal += (cpu_val_n) / 2;
+
+ pDCTstat->CH_MaxRdLat[Channel] = SubTotal;
+ if(pDCTstat->GangedMode) {
+@@ -659,143 +851,6 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQ
+ Set_NB32(dev, reg, val);
+ }
+
+-static u8 mct_SavePassRcvEnDly_D(struct DCTStatStruc *pDCTstat,
+- u8 rcvrEnDly, u8 Channel,
+- u8 receiver, u8 Pass)
+-{
+- u8 i;
+- u8 mask_Saved, mask_Pass;
+- u8 *p;
+-
+- /* calculate dimm offset
+- * not needed for CH_D_B_RCVRDLY array
+- */
+-
+- /* cmp if there has new DqsRcvEnDly to be recorded */
+- mask_Pass = pDCTstat->DqsRcvEn_Pass;
+-
+- if(Pass == SecondPass) {
+- mask_Pass = ~mask_Pass;
+- }
+-
+- mask_Saved = pDCTstat->DqsRcvEn_Saved;
+- if(mask_Pass != mask_Saved) {
+-
+- /* find desired stack offset according to channel/dimm/byte */
+- if(Pass == SecondPass) {
+- /* FIXME: SecondPass is never used for Barcelona p = pDCTstat->CH_D_B_RCVRDLY_1[Channel][receiver>>1]; */
+- p = 0; /* Keep the compiler happy. */
+- } else {
+- mask_Saved &= mask_Pass;
+- p = pDCTstat->CH_D_B_RCVRDLY[Channel][receiver>>1];
+- }
+- for(i=0; i < 8; i++) {
+- /* cmp per byte lane */
+- if(mask_Pass & (1 << i)) {
+- if(!(mask_Saved & (1 << i))) {
+- /* save RcvEnDly to stack, according to
+- the related Dimm/byte lane */
+- p[i] = (u8)rcvrEnDly;
+- mask_Saved |= 1 << i;
+- }
+- }
+- }
+- pDCTstat->DqsRcvEn_Saved = mask_Saved;
+- }
+- return mct_SaveRcvEnDly_D_1Pass(pDCTstat, Pass);
+-}
+-
+-static u8 mct_CompareTestPatternQW0_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat,
+- u32 addr, u8 channel,
+- u8 pattern, u8 Pass)
+-{
+- /* Compare only the first beat of data. Since target addrs are cache
+- * line aligned, the Channel parameter is used to determine which
+- * cache QW to compare.
+- */
+-
+- u8 *test_buf;
+- u8 i;
+- u8 result;
+- u8 value;
+-
+- if(Pass == FirstPass) {
+- if(pattern==1) {
+- test_buf = (u8 *)TestPattern1_D;
+- } else {
+- test_buf = (u8 *)TestPattern0_D;
+- }
+- } else { /* Second Pass */
+- test_buf = (u8 *)TestPattern2_D;
+- }
+-
+- SetUpperFSbase(addr);
+- addr <<= 8;
+-
+- if((pDCTstat->Status & (1<<SB_128bitmode)) && channel ) {
+- addr += 8; /* second channel */
+- test_buf += 8;
+- }
+-
+- print_debug_dqs_pair("\t\t\t\t\t\t test_buf = ", (u32)test_buf, " | addr_lo = ", addr, 4);
+- for (i=0; i<8; i++, addr ++) {
+- value = read32_fs(addr);
+- print_debug_dqs_pair("\t\t\t\t\t\t\t\t ", test_buf[i], " | ", value, 4);
+-
+- if (value == test_buf[i]) {
+- pDCTstat->DqsRcvEn_Pass |= (1<<i);
+- } else {
+- pDCTstat->DqsRcvEn_Pass &= ~(1<<i);
+- }
+- }
+-
+- result = DQS_FAIL;
+-
+- if (Pass == FirstPass) {
+- /* if first pass, at least one byte lane pass
+- * ,then DQS_PASS=1 and will set to related reg.
+- */
+- if(pDCTstat->DqsRcvEn_Pass != 0) {
+- result = DQS_PASS;
+- } else {
+- result = DQS_FAIL;
+- }
+-
+- } else {
+- /* if second pass, at least one byte lane fail
+- * ,then DQS_FAIL=1 and will set to related reg.
+- */
+- if(pDCTstat->DqsRcvEn_Pass != 0xFF) {
+- result = DQS_FAIL;
+- } else {
+- result = DQS_PASS;
+- }
+- }
+-
+- /* if second pass, we can't find the fail until FFh,
+- * then let it fail to save the final delay
+- */
+- if((Pass == SecondPass) && (pDCTstat->Status & (1 << SB_DQSRcvLimit))) {
+- result = DQS_FAIL;
+- pDCTstat->DqsRcvEn_Pass = 0;
+- }
+-
+- /* second pass needs to be inverted
+- * FIXME? this could be inverted in the above code to start with...
+- */
+- if(Pass == SecondPass) {
+- if (result == DQS_PASS) {
+- result = DQS_FAIL;
+- } else if (result == DQS_FAIL) { /* FIXME: doesn't need to be else if */
+- result = DQS_PASS;
+- }
+- }
+-
+-
+- return result;
+-}
+-
+ static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+@@ -854,7 +909,7 @@ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
+ u32 index_reg;
+ u32 index;
+ u8 ChipSel;
+- u8 *p;
++ u16 *p;
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+@@ -884,7 +939,7 @@ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, ChipSel)) {
+- u8 *p;
++ u16 *p;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][ChipSel>>1];
+
+ /* DQS Delay Value of Data Bytelane
+@@ -920,6 +975,10 @@ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ SetEccDQSRcvrEn_D(pDCTstat, Channel);
+ }
+
++/* 2.8.9.9.4
++ * ECC Byte Lane Training
++ * DQS Receiver Enable Delay
++ */
+ void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1017,7 +1076,9 @@ static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ avRecValue -= 3;
+ else
+ */
+- if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
++ if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
++ avRecValue -= 8;
++ else if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+ avRecValue -= 8;
+ else if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
+ avRecValue -= 8;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
+index c009756..f01e011 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -36,17 +37,12 @@ u32 SetupDqsPattern_1PassB(u8 pass)
+ return (u32) TestPattern0_D;
+ }
+
+-u8 mct_Get_Start_RcvrEnDly_1Pass(u8 pass)
+-{
+- return 0;
+-}
+-
+-static u8 mct_Average_RcvrEnDly_1Pass(struct DCTStatStruc *pDCTstat, u8 Channel, u8 Receiver,
++static u16 mct_Average_RcvrEnDly_1Pass(struct DCTStatStruc *pDCTstat, u8 Channel, u8 Receiver,
+ u8 Pass)
+ {
+- u8 i, MaxValue;
+- u8 *p;
+- u8 val;
++ u16 i, MaxValue;
++ u16 *p;
++ u16 val;
+
+ MaxValue = 0;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver >> 1];
+@@ -76,8 +72,8 @@ u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass)
+ return ret;
+ }
+
+-u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+- u8 RcvrEnDly, u8 RcvrEnDlyLimit,
++u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
++ u16 RcvrEnDly, u16 RcvrEnDlyLimit,
+ u8 Channel, u8 Receiver, u8 Pass)
+
+ {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c
+index b01889d..796febc 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -74,15 +75,15 @@ u8 mct_Get_Start_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+ return RcvrEnDly;
+ }
+
+-u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+- u8 RcvrEnDly, u8 RcvrEnDlyLimit,
++u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
++ u16 RcvrEnDly, u16 RcvrEnDlyLimit,
+ u8 Channel, u8 Receiver, u8 Pass)
+ {
+ u8 i;
+- u8 *p;
+- u8 *p_1;
+- u8 val;
+- u8 val_1;
++ u16 *p;
++ u16 *p_1;
++ u16 val;
++ u16 val_1;
+ u8 valid = 1;
+ u8 bn;
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
+index ea5c8c7..920f514 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -191,10 +192,10 @@ static void maxRdLatencyTrain_D(struct MCTStatStruc *pMCTstat,
+
+ #if DQS_TRAIN_DEBUG > 0
+ {
+- u8 Channel;
++ u8 ChannelDTD;
+ printk(BIOS_DEBUG, "maxRdLatencyTrain: CH_MaxRdLat:\n");
+- for(Channel = 0; Channel<2; Channel++) {
+- printk(BIOS_DEBUG, "Channel: %02x: %02x\n", Channel, pDCTstat->CH_MaxRdLat[Channel]);
++ for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel: %02x: %02x\n", ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
+ }
+ }
+ #endif
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+index cdeae49..1c3e322 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -58,9 +59,9 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+ pDCTstat->C_DCTPtr[dct]->LogicalCPUID = pDCTstat->LogicalCPUID;
+
+ for (dimm = 0; dimm < MAX_DIMMS; dimm++) {
+- if (DimmValid & (1 << dimm))
++ if (DimmValid & (1 << (dimm << 1)))
+ pDCTstat->C_DCTPtr[dct]->DimmPresent[dimm] = 1;
+- if (Dimmx8Present & (1 << dimm))
++ if (Dimmx8Present & (1 << (dimm << 1)))
+ pDCTstat->C_DCTPtr[dct]->DimmX8Present[dimm] = 1;
+ }
+
+@@ -88,9 +89,9 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+ u8 DimmRanks;
+ if (DimmValid & (1 << (dimm << 1))) {
+ DimmRanks = 1;
+- if (pDCTstat->DimmDRPresent & (1 << (dimm+dct)))
++ if (pDCTstat->DimmDRPresent & (1 << ((dimm << 1) + dct)))
+ DimmRanks = 2;
+- else if (pDCTstat->DimmQRPresent & (1 << (dimm+dct)))
++ else if (pDCTstat->DimmQRPresent & (1 << ((dimm << 1) + dct)))
+ DimmRanks = 4;
+ } else
+ DimmRanks = 0;
+@@ -249,35 +250,6 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+-/* Multiply the previously saved delay values in Pass 1, step #5 by
+- (target frequency)/400 to find the gross and fine delay initialization
+- values at the target frequency.
+- */
+-void MultiplyDelay(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
+-{
+- u16 index;
+- u8 Multiplier;
+- u8 gross, fine;
+- u16 total;
+-
+- Multiplier = pDCTstat->TargetFreq;
+-
+- for (index=0; index < MAX_BYTE_LANES*MAX_LDIMMS; index ++) {
+- gross = pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index];
+- fine = pDCTstat->C_DCTPtr[dct]->WLFineDelay[index];
+-
+- total = gross << 5 | fine;
+- total *= Multiplier;
+- if (total % 3)
+- total = total / 3 + 1;
+- else
+- total = total / 3;
+- pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index] = (total & 0xFF) >> 5;
+- pDCTstat->C_DCTPtr[dct]->WLFineDelay[index] = total & 0x1F;
+- }
+-}
+-
+ /*
+ * the DRAM controller to bring the DRAMs out of self refresh mode.
+ */
+@@ -352,9 +324,9 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+
+ if (!DCT1Present)
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
+- else if (pDCTstat->GangedMode) {
++ else if (pDCTstat->GangedMode)
+ pDCTstat->CSPresent = 0;
+- } else
++ else
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[1];
+
+ FreqChgCtrlWrd(pMCTstat, pDCTstat);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 212a348..c76476b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -235,6 +236,65 @@ u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
+ return MRSValue;
+ }
+
++static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
++{
++ uint16_t term;
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (number_of_dimms == 1) {
++ if (MaxDimmsInstallable < 3) {
++ term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
++ } else {
++ if (rank_count == 1) {
++ term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
++ } else {
++ if (rank == 0)
++ term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
++ else
++ term = 0x00; /* Rtt_Nom=OFF */
++ }
++ }
++ } else {
++ if (frequency_index < 5)
++ term = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
++ else
++ term = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
++ }
++
++ return term;
++}
++
++static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
++{
++ uint16_t term;
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (number_of_dimms == 1) {
++ if (MaxDimmsInstallable < 3) {
++ term = 0x00; /* Rtt_WR=off */
++ } else {
++ if (rank_count == 1)
++ term = 0x00; /* Rtt_WR=off */
++ else
++ term = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
++ }
++ } else {
++ term = 0x400; /* Rtt_WR=RZQ/2=120 Ohm */
++ }
++
++ return term;
++}
++
+ /*-----------------------------------------------------------------------------
+ * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
+ *
+@@ -295,48 +355,23 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+ } else {
+- if (wl)
+- {
+- if (pDCTData->MaxDimmsInstalled == 1)
+- {
+- if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0))
+- {
+- tempW1 = 0x00; /* Rtt_Nom=OFF */
+- }
++ if (wl) {
++ if (rank == 0) {
++ /* Get Rtt_WR for the current DIMM and rank */
++ uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++
++ /* Convert dynamic termination code to corresponding nominal termination code */
++ if (dynamic_term == 0x200)
++ tempW1 = 0x04;
++ else if (dynamic_term == 0x400)
++ tempW1 = 0x40;
+ else
+- {
+- tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+- }
+- }
+- else /* 2 Dimms or more per channel */
+- {
+- if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1))
+- {
+- tempW1 = 0x00; /* Rtt_Nom=OFF */
+- }
+- else
+- {
+- if (MemClkFreq == 6) {
+- tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+- } else {
+- tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */
+- }
+- }
+- }
+- }
+- else { /* 1 or 4 Dimms per channel */
+- if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4))
+- {
+- tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+- }
+- else /* 2 or 3 Dimms per channel */
+- {
+- if (MemClkFreq < 5) {
+- tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
+- } else {
+- tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
+- }
++ tempW1 = 0x0;
++ } else {
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+ }
++ } else {
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+ }
+ }
+ tempW=tempW|tempW1;
+@@ -353,20 +388,22 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ else
+ {
+ /* Disable the output drivers of all other ranks for
+- * the target DIMM. */
++ * the target DIMM.
++ */
+ tempW = bitTestSet(tempW1, Qoff);
+ }
+ }
+- /* program MrsAddress[5,1]=output driver impedance control (DIC):
+- * based on F2x[1,0]84[DrvImpCtrl] */
++ /* Program MrsAddress[5,1]=output driver impedance control (DIC):
++ * based on F2x[1,0]84[DrvImpCtrl]
++ */
+ tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+ FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
+- if (bitTest(tempW1,1))
+- {tempW = bitTestSet(tempW, 5);}
+- if (bitTest(tempW1,0))
+- {tempW = bitTestSet(tempW, 1);}
++ if (bitTest(tempW1, 1))
++ tempW = bitTestSet(tempW, 5);
++ if (bitTest(tempW1, 0))
++ tempW = bitTestSet(tempW, 1);
+
+- tempW = swapAddrBits_wl(pDCTData,tempW);
++ tempW = swapAddrBits_wl(pDCTData, tempW);
+
+ set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+ DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
+@@ -404,29 +441,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+ tempW+=0x8;
+ /* determine Rtt_WR for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+- } else {
+- if (wl)
+- {
+- tempW1 = 0x00; /* Rtt_WR=off */
+- }
+- else
+- {
+- if (pDCTData->MaxDimmsInstalled == 1)
+- {
+- tempW1 = 0x00; /* Rtt_WR=off */
+- }
+- else
+- {
+- if (MemClkFreq == 6) {
+- tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
+- } else {
+- tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
+- }
+- }
+- }
+- }
++ else
++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+ tempW=tempW|tempW1;
+ tempW = swapAddrBits_wl(pDCTData,tempW);
+ set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+@@ -483,38 +501,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ }
+
+ /* determine Rtt_Nom for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+- } else {
+- if (wl)
+- {
+- if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1))
+- {
+- tempW1 = 0x00; /* Rtt_Nom=OFF */
+- }
+- else
+- {
+- if (MemClkFreq < 5) {
+- tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */
+- } else {
+- tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */
+- }
+- }
+- }
+- else { /* 1 or 4 Dimms per channel */
+- if (pDCTData->MaxDimmsInstalled == 4)
+- {
+- tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+- }
+- else { /* 2 or 3 Dimms per channel */
+- if (MemClkFreq < 5) {
+- tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
+- } else {
+- tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
+- }
+- }
+- }
+- }
++ else
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+ tempW=tempW|tempW1;
+ /* program MrsAddress[5,1]=output driver impedance control (DIC):
+ * based on F2x[1,0]84[DrvImpCtrl] */
+@@ -560,22 +550,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+ tempW+=0x8;
+ /* determine Rtt_WR for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+- } else {
+- if (wl)
+- {
+- tempW1 = 0x00; /* Rtt_WR=off */
+- }
+- else
+- {
+- if (MemClkFreq == 6) {
+- tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
+- } else {
+- tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
+- }
+- }
+- }
++ else
++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+ tempW=tempW|tempW1;
+ tempW = swapAddrBits_wl(pDCTData,tempW);
+ set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+@@ -646,9 +624,14 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+ */
+ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ {
+- u8 ByteLane, Seed_Gross, Seed_Fine;
++ u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq;
+ u32 Value, Addr;
+ u16 Addl_Data_Offset, Addl_Data_Port;
++ u16 freq_tab[] = {400, 533, 667, 800};
++
++ /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
++ MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
+
+ /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
+ * current memory subsystem configuration.
+@@ -656,12 +639,13 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ programODT(pMCTData, pDCTData, dimm);
+
+ /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
+- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) {
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, (u32)1);
++ }
+ else
+ {
+- /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B*/
++ /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */
+ if (pDCTData->DctTrain)
+ {
+ Addl_Data_Offset=0x198;
+@@ -687,7 +671,6 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+
+ /* Wait 10 MEMCLKs to allow for ODT signal settling. */
+ pMCTData->AgesaDelay(10);
+- ByteLane = 0;
+ if (pass == 1)
+ {
+ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+@@ -705,10 +688,17 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ }
+ else
+ {
+- Seed_Gross = 0x00;
+- Seed_Fine = 0x1A;
++ if (MemClkFreq == 6) {
++ /* DDR-800 */
++ Seed_Gross = 0x00;
++ Seed_Fine = 0x1a;
++ } else {
++ /* Use settings for DDR-400 (interpolated from BKDG) */
++ Seed_Gross = 0x00;
++ Seed_Fine = 0x0d;
++ }
+ }
+- while(ByteLane < MAX_BYTE_LANES)
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+ {
+ /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
+ * F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields
+@@ -720,35 +710,32 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ */
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+- ByteLane++;
+ }
+- } else if (pDCTData->Status[DCT_STATUS_REGISTERED]) { /* For Pass 2 */
++ } else { /* Pass 2 */
+ /* From BKDG, Write Leveling Seed Value. */
+- /* TODO: The unbuffered DIMMs are unstable on the code below. So temporarily it is
+- * only for registered DIMMs. */
+ u32 RegisterDelay, SeedTotal;
+- u8 MemClkFreq;
+- u16 freq_tab[] = {400, 533, 667, 800};
+- while(ByteLane < MAX_BYTE_LANES)
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+ {
+- MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
+ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
+ else
+ RegisterDelay = 0;
+- SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1F) |
+- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5;
++ SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
+ /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
+ training) - RegisterDelay. */
+- /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
+- SeedTotal = (u16) (RegisterDelay + ((((u32) SeedTotal - RegisterDelay) *
+- freq_tab[MemClkFreq-3]) / 400));
+- Seed_Gross = (SeedTotal & 0x20) != 0 ? 1 : 2;
+- Seed_Fine = SeedTotal & 0x1F;
++ SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
++ freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 100)));
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal & 0x1f;
++ if (Seed_Gross == 0)
++ Seed_Gross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_Gross = 1;
++ else
++ Seed_Gross = 2;
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+- ByteLane ++;
+ }
+ }
+
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index ea32893..47260f2 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -49,7 +49,7 @@ static const uint16_t ddr3_limits[4] = {800, 666, 533, 400};
+ static u16 mctGet_NVbits(u8 index)
+ {
+ u16 val = 0;
+- int nvram;
++ int nvram = 0;
+
+ switch (index) {
+ case NV_PACK_TYPE:
+@@ -59,6 +59,10 @@ static u16 mctGet_NVbits(u8 index)
+ val = 1;
+ #elif CONFIG_CPU_SOCKET_TYPE == 0x13 /* ASB2 */
+ val = 4;
++#elif CONFIG_CPU_SOCKET_TYPE == 0x14 /* C32 */
++ val = 5;
++#elif CONFIG_CPU_SOCKET_TYPE == 0x15 /* G34 */
++ val = 3;
+ //#elif SYSTEM_TYPE == MOBILE
+ // val = 2;
+ #endif
+@@ -297,6 +301,8 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ /* Determine the number of installed DIMMs */
+ int ch1_count = 0;
+ int ch2_count = 0;
++ uint8_t ch1_registered = 0;
++ uint8_t ch2_registered = 0;
+ int i;
+ for (i = 0; i < 15; i = i + 2) {
+ if (pDCTstat->DIMMValid & (1 << i))
+@@ -304,13 +310,19 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ if (pDCTstat->DIMMValid & (1 << (i + 1)))
+ ch2_count++;
+ }
++ for (i = 0; i < MAX_DIMMS_SUPPORTED; i = i + 2) {
++ if (pDCTstat->DimmRegistered[i])
++ ch1_registered = 1;
++ if (pDCTstat->DimmRegistered[i + 1])
++ ch2_registered = 1;
++ }
+ if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP)) {
+ printk(BIOS_DEBUG, "mctGet_MaxLoadFreq: Channel 1: %d DIMM(s) detected\n", ch1_count);
+ printk(BIOS_DEBUG, "mctGet_MaxLoadFreq: Channel 2: %d DIMM(s) detected\n", ch2_count);
+ }
+
+ /* Set limits if needed */
+- pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), pDCTstat->PresetmaxFreq);
++ pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), (ch1_registered || ch2_registered), pDCTstat->PresetmaxFreq);
+ }
+
+ #ifdef UNUSED_CODE
+@@ -413,101 +425,6 @@ static void mctHookAfterDramInit(void)
+ }
+
+ #if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+-static void coreDelay(u32 microseconds)
+-{
+- msr_t now;
+- msr_t end;
+- u32 cycles;
+-
+- /* delay ~40us
+- This seems like a hack to me...
+- It would be nice to have a central delay function. */
+-
+- cycles = (microseconds * 100) << 3; /* x8 (number of 1.25ns ticks) */
+-
+- if (!(rdmsr(HWCR).lo & TSC_FREQ_SEL_MASK)) {
+- msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
+- if (!(rdmsr(0xC0010064+pstate_msr.lo).lo & NB_DID_M_ON)) {
+- cycles = cycles <<1; // half freq, double cycles
+- }
+- } // else should we keep p0 freq at the time of setting TSC_FREQ_SEL_MASK somewhere and check it here ?
+-
+- now = rdmsr(TSC_MSR);
+- // avoid overflow when called near 2^32 ticks ~ 5.3 s boundaries
+- if (0xffffffff - cycles >= now.lo ) {
+- end.hi = now.hi;
+- end.lo = now.lo + cycles;
+- } else {
+- end.hi = now.hi +1; //
+- end.lo = cycles - (1+(0xffffffff - now.lo));
+- }
+- do {
+- now = rdmsr(TSC_MSR);
+- } while ((now.hi < end.hi) || ((now.hi == end.hi) && (now.lo < end.lo)));
+-}
+-
+-/* Erratum 350 */
+-static void vErrata350(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+-{
+- u8 u8Channel;
+- u8 u8Receiver;
+- u32 u32Addr;
+- u8 u8Valid;
+- u32 u32DctDev;
+-
+- // 1. dummy read for each installed DIMM */
+- for (u8Channel = 0; u8Channel < 2; u8Channel++) {
+- // This will be 0 for vaild DIMMS, eles 8
+- u8Receiver = mct_InitReceiver_D(pDCTstat, u8Channel);
+-
+- for (; u8Receiver < 8; u8Receiver += 2) {
+- u32Addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, u8Channel, u8Receiver, &u8Valid);
+-
+- if(!u8Valid) { /* Address not supported on current CS */
+- print_t("vErrata350: Address not supported on current CS\n");
+- continue;
+- }
+- print_t("vErrata350: dummy read \n");
+- read32_fs(u32Addr);
+- }
+- }
+-
+- print_t("vErrata350: step 2a\n");
+-
+- /* 2. Write 0000_8000h to register F2x[1, 0]9C_xD080F0C. */
+- u32DctDev = pDCTstat->dev_dct;
+- Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00008000);
+- /* ^--- value
+- ^---F2x[1, 0]9C_x0D080F0C, No description in BKDG.
+- ^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */
+-
+- if(!pDCTstat->GangedMode) {
+- print_t("vErrata350: step 2b\n");
+- Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00008000);
+- /* ^--- value
+- ^---F2x[1, 0]9C_x0D080F0C, No description in BKDG
+- ^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */
+- }
+-
+- print_t("vErrata350: step 3\n");
+- /* 3. Wait at least 300 nanoseconds. */
+- coreDelay(1);
+-
+- print_t("vErrata350: step 4\n");
+- /* 4. Write 0000_0000h to register F2x[1, 0]9C_xD080F0C. */
+- Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00000000);
+-
+- if(!pDCTstat->GangedMode) {
+- print_t("vErrata350: step 4b\n");
+- Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00000000);
+- }
+-
+- print_t("vErrata350: step 5\n");
+- /* 5. Wait at least 2 microseconds. */
+- coreDelay(2);
+-
+-}
+-
+ static void vErratum372(struct DCTStatStruc *pDCTstat)
+ {
+ msr_t msr = rdmsr(NB_CFG_MSR);
+@@ -546,8 +463,7 @@ static void mctHookBeforeAnyTraining(struct MCTStatStruc *pMCTstat, struct DCTSt
+ {
+ #if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+ /* FIXME : as of 25.6.2010 errata 350 and 372 should apply to ((RB|BL|DA)-C[23])|(HY-D[01])|(PH-E0) but I don't find constants for all of them */
+- if (pDCTstatA->LogicalCPUID & AMD_DRBH_Cx) {
+- vErrata350(pMCTstat, pDCTstatA);
++ if (pDCTstatA->LogicalCPUID & (AMD_DRBH_Cx | AMD_DR_Dx)) {
+ vErratum372(pDCTstatA);
+ vErratum414(pDCTstatA);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0012-northbridge-amd-amdmct-mct_ddr3-Fix-curly-brace-styl.patch b/resources/libreboot/patch/kgpe-d16/0012-northbridge-amd-amdmct-mct_ddr3-Fix-curly-brace-styl.patch
new file mode 100644
index 00000000..7e62ea30
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0012-northbridge-amd-amdmct-mct_ddr3-Fix-curly-brace-styl.patch
@@ -0,0 +1,93 @@
+From 7192af06922df114da15077d51e8882d3d10f101 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 8 Sep 2015 16:08:45 -0500
+Subject: [PATCH 012/146] northbridge/amd/amdmct/mct_ddr3: Fix curly brace
+ style violations
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 37 +++++++------------------
+ 1 file changed, 10 insertions(+), 27 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index c76476b..9f42d54 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -818,28 +818,19 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+
+ tempB = 0;
+ offsetAddr = (u8)(3 * dimm);
+- if (ByteLane < 2)
+- {
++ if (ByteLane < 2) {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01;
+- }
+- else if (ByteLane <4)
+- {
++ } else if (ByteLane <4) {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 1;
+- }
+- else if (ByteLane <6)
+- {
++ } else if (ByteLane <6) {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45;
+- }
+- else if (ByteLane <8)
+- {
++ } else if (ByteLane <8) {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 + 1;
+- }
+- else
+- {
++ } else {
+ tempB = 0;
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2;
+ }
+@@ -883,19 +874,14 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
+ u32 addr, fine, gross;
+ tempB = 0;
+ index = (u8)(MAX_BYTE_LANES*dimm);
+- if (ByteLane < 4)
+- {
++ if (ByteLane < 4) {
+ tempB = (u8)(8 * ByteLane);
+ addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW;
+- }
+- else if (ByteLane < 8)
+- {
++ } else if (ByteLane < 8) {
+ tempB1 = (u8)(ByteLane - 4);
+ tempB = (u8)(8 * tempB1);
+ addr = DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH;
+- }
+- else
+- {
++ } else {
+ tempB = 0;
+ addr = DRAM_CONT_ADD_ECC_PHASE_REC_CTRL;
+ }
+@@ -911,16 +897,13 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
+ /* Adjust seed gross delay overflow (greater than 3):
+ * - Adjust the trained gross delay to the original seed gross delay.
+ */
+- if(pDCTData->WLGrossDelay[index+ByteLane] >= 3)
+- {
++ if (pDCTData->WLGrossDelay[index+ByteLane] >= 3) {
+ gross += pDCTData->WLGrossDelay[index+ByteLane];
+ if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
+ gross -= 1;
+ else
+ gross -= 2;
+- }
+- else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3))
+- {
++ } else if ((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) {
+ /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
+ * We will then round the negative number to 0.
+ */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0013-northbridge-amd-amdfam10-Limit-maximum-RAM-clock-to-.patch b/resources/libreboot/patch/kgpe-d16/0013-northbridge-amd-amdfam10-Limit-maximum-RAM-clock-to-.patch
new file mode 100644
index 00000000..0d507c7f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0013-northbridge-amd-amdfam10-Limit-maximum-RAM-clock-to-.patch
@@ -0,0 +1,120 @@
+From 818a658a290203625a65df4dde4901ea66fd72c8 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:00:27 -0500
+Subject: [PATCH 013/146] northbridge/amd/amdfam10: Limit maximum RAM clock to
+ BKDG recommendations
+
+---
+ src/northbridge/amd/amdfam10/raminit_amdmct.c | 53 +++++++++++++++++++------
+ 1 file changed, 40 insertions(+), 13 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+index a0d47f4..fa14e4f 100644
+--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
++++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+@@ -28,13 +28,6 @@ static void print_tx(const char *strval, u32 val)
+ }
+ #endif
+
+-static void print_t(const char *strval)
+-{
+-#if CONFIG_DEBUG_RAM_SETUP
+- printk(BIOS_DEBUG, "%s", strval);
+-#endif
+-}
+-
+ static void print_tf(const char *func, const char *strval)
+ {
+ #if CONFIG_DEBUG_RAM_SETUP
+@@ -42,30 +35,59 @@ static void print_tf(const char *func, const char *strval)
+ #endif
+ }
+
+-static uint16_t mct_MaxLoadFreq(uint8_t count, uint16_t freq)
++static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq)
+ {
+ /* Return limited maximum RAM frequency */
+ if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
+- if (IS_ENABLED(CONFIG_DIMM_REGISTERED)) {
++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
+ /* K10 BKDG Rev. 3.62 Table 53 */
+ if (count > 2) {
+ /* Limit to DDR2-533 */
+ if (freq > 266) {
+ freq = 266;
+- print_tf(__func__, ": More than 2 DIMMs on channel; limiting to DDR2-533\n");
++ print_tf(__func__, ": More than 2 registered DIMMs on channel; limiting to DDR2-533\n");
+ }
+ }
+- }
+- else {
++ } else {
+ /* K10 BKDG Rev. 3.62 Table 52 */
+ if (count > 1) {
+ /* Limit to DDR2-800 */
+ if (freq > 400) {
+ freq = 400;
+- print_tf(__func__, ": More than 1 DIMM on channel; limiting to DDR2-800\n");
++ print_tf(__func__, ": More than 1 unbuffered DIMM on channel; limiting to DDR2-800\n");
+ }
+ }
+ }
++ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
++ /* K10 BKDG Rev. 3.62 Table 34 */
++ if (count > 2) {
++ /* Limit to DDR3-800 */
++ if (freq > 400) {
++ freq = 400;
++ print_tf(__func__, ": More than 2 registered DIMMs on channel; limiting to DDR3-800\n");
++ }
++ } else if (count == 2) {
++ /* Limit to DDR3-1066 */
++ if (freq > 533) {
++ freq = 533;
++ print_tf(__func__, ": 2 registered DIMMs on channel; limiting to DDR3-1066\n");
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ print_tf(__func__, ": 1 registered DIMM on channel; limiting to DDR3-1333\n");
++ }
++ }
++ } else {
++ /* K10 BKDG Rev. 3.62 Table 33 */
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ print_tf(__func__, ": unbuffered DIMMs on channel; limiting to DDR3-1333\n");
++ }
++ }
+ }
+
+ return freq;
+@@ -118,6 +140,9 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint16_t freq)
+ //C32
+ #elif CONFIG_CPU_SOCKET_TYPE == 0x14
+ #include "../amdmct/mct_ddr3/mctardk5.c"
++//G34
++#elif CONFIG_CPU_SOCKET_TYPE == 0x15
++#include "../amdmct/mct_ddr3/mctardk5.c"
+ #endif
+
+ #else /* DDR2 */
+@@ -205,6 +230,7 @@ static void raminit_amdmct(struct sys_info *sysinfo)
+ printk(BIOS_DEBUG, "raminit_amdmct end:\n");
+ }
+
++#if !IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
+ static void amdmct_cbmem_store_info(struct sys_info *sysinfo)
+ {
+ if (!sysinfo)
+@@ -243,3 +269,4 @@ static void amdmct_cbmem_store_info(struct sys_info *sysinfo)
+ }
+ #endif
+ }
++#endif
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0014-northbridge-amd-amdfam10-Fix-typo-in-comment.patch b/resources/libreboot/patch/kgpe-d16/0014-northbridge-amd-amdfam10-Fix-typo-in-comment.patch
new file mode 100644
index 00000000..85b258ac
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0014-northbridge-amd-amdfam10-Fix-typo-in-comment.patch
@@ -0,0 +1,33 @@
+From d5701700c4c905866a58c600b7a9f1a7f534e5f6 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:01:31 -0500
+Subject: [PATCH 014/146] northbridge/amd/amdfam10: Fix typo in comment
+
+---
+ src/northbridge/amd/amdfam10/misc_control.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index e242e34..85c8838 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2003 by Eric Biederman
+ * Copyright (C) Stefan Reinauer
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -47,7 +48,7 @@
+ * The same trick can be used to augment legacy VGA resources which can
+ * be detect by generic pci reousrce allocator for VGA devices.
+ * BAD: it is more tricky than I think, the resource allocation code is
+- * implemented in a way to NOT DOING legacy VGA resource allcation on
++ * implemented in a way to NOT DOING legacy VGA resource allocation on
+ * purpose :-(.
+ */
+ static void mcf3_read_resources(device_t dev)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0015-device-hypertransport-Add-additional-debug-output.patch b/resources/libreboot/patch/kgpe-d16/0015-device-hypertransport-Add-additional-debug-output.patch
new file mode 100644
index 00000000..1ca8d3aa
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0015-device-hypertransport-Add-additional-debug-output.patch
@@ -0,0 +1,33 @@
+From c8c17707af0850b4e520a2616e217e9fae2cf0e3 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:06:52 -0500
+Subject: [PATCH 015/146] device/hypertransport: Add additional debug output
+
+---
+ src/device/hypertransport.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/device/hypertransport.c b/src/device/hypertransport.c
+index 07a320d..c76cb21 100644
+--- a/src/device/hypertransport.c
++++ b/src/device/hypertransport.c
+@@ -8,6 +8,7 @@
+ * Copyright (C) 2005-2006 Tyan
+ * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan)
+ * Copyright (C) 2005-2006 Stefan Reinauer <stepan@openbios.org>
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -263,6 +264,8 @@ static unsigned int do_hypertransport_scan_chain(struct bus *bus, unsigned min_d
+ struct ht_link prev;
+ int ht_dev_num = 0;
+
++ printk(BIOS_SPEW, "%s for bus %02x\n", __func__, bus->secondary);
++
+ min_unitid = (offset_unitid) ? CONFIG_HT_CHAIN_UNITID_BASE : 1;
+
+ #if CONFIG_HT_CHAIN_END_UNITID_BASE != 0x20
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0016-mainboard-asus-kgpe-d16-Add-initial-support-for-the-.patch b/resources/libreboot/patch/kgpe-d16/0016-mainboard-asus-kgpe-d16-Add-initial-support-for-the-.patch
new file mode 100644
index 00000000..53b7e196
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0016-mainboard-asus-kgpe-d16-Add-initial-support-for-the-.patch
@@ -0,0 +1,3218 @@
+From ef853baaaec3bf5aee31f443a7930f9882e3a01c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 30 Apr 2015 01:47:31 -0500
+Subject: [PATCH 016/146] mainboard/asus/kgpe-d16: Add initial support for the
+ KGPE-D16
+
+As of this commit S3 suspend does not work on any K10 boards,
+including this board.
+---
+ src/mainboard/asus/kgpe-d16/Kconfig | 95 ++++
+ src/mainboard/asus/kgpe-d16/Kconfig.name | 2 +
+ src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl | 367 +++++++++++++
+ src/mainboard/asus/kgpe-d16/acpi_tables.c | 75 +++
+ src/mainboard/asus/kgpe-d16/board_info.txt | 5 +
+ src/mainboard/asus/kgpe-d16/bootblock.c | 52 ++
+ src/mainboard/asus/kgpe-d16/cmos.default | 16 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 134 +++++
+ src/mainboard/asus/kgpe-d16/devicetree.cb | 248 +++++++++
+ src/mainboard/asus/kgpe-d16/dsdt.asl | 730 ++++++++++++++++++++++++++
+ src/mainboard/asus/kgpe-d16/get_bus_conf.c | 128 +++++
+ src/mainboard/asus/kgpe-d16/irq_tables.c | 112 ++++
+ src/mainboard/asus/kgpe-d16/mainboard.c | 81 +++
+ src/mainboard/asus/kgpe-d16/mb_sysconf.h | 44 ++
+ src/mainboard/asus/kgpe-d16/mptable.c | 231 ++++++++
+ src/mainboard/asus/kgpe-d16/resourcemap.c | 284 ++++++++++
+ src/mainboard/asus/kgpe-d16/romstage.c | 422 +++++++++++++++
+ src/mainboard/asus/kgpe-d16/spd_notes.txt | 30 ++
+ 18 files changed, 3056 insertions(+)
+ create mode 100644 src/mainboard/asus/kgpe-d16/Kconfig
+ create mode 100644 src/mainboard/asus/kgpe-d16/Kconfig.name
+ create mode 100644 src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
+ create mode 100644 src/mainboard/asus/kgpe-d16/acpi_tables.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/board_info.txt
+ create mode 100644 src/mainboard/asus/kgpe-d16/bootblock.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/cmos.default
+ create mode 100644 src/mainboard/asus/kgpe-d16/cmos.layout
+ create mode 100644 src/mainboard/asus/kgpe-d16/devicetree.cb
+ create mode 100644 src/mainboard/asus/kgpe-d16/dsdt.asl
+ create mode 100644 src/mainboard/asus/kgpe-d16/get_bus_conf.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/irq_tables.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/mainboard.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/mb_sysconf.h
+ create mode 100644 src/mainboard/asus/kgpe-d16/mptable.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/resourcemap.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/romstage.c
+ create mode 100644 src/mainboard/asus/kgpe-d16/spd_notes.txt
+
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+new file mode 100644
+index 0000000..95b3b5b
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -0,0 +1,95 @@
++if BOARD_ASUS_KGPE_D16
++
++config BOARD_SPECIFIC_OPTIONS # dummy
++ def_bool y
++ select CPU_AMD_SOCKET_G34_NON_AGESA
++ select DIMM_DDR3
++ select DIMM_REGISTERED
++ # select QRANK_DIMM_SUPPORT
++ select NORTHBRIDGE_AMD_AMDFAM10
++ select SOUTHBRIDGE_AMD_SR5650
++ select SOUTHBRIDGE_AMD_SB700
++ select SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA
++ select SUPERIO_NUVOTON_NCT5572D
++ select PARALLEL_CPU_INIT
++ select HAVE_HARD_RESET
++ select HAVE_OPTION_TABLE
++ select HAVE_CMOS_DEFAULT
++ select HAVE_PIRQ_TABLE
++ select HAVE_MP_TABLE
++ select HAVE_ACPI_TABLES
++ select SB_HT_CHAIN_UNITID_OFFSET_ONLY
++ select LIFT_BSP_APIC_ID
++ select BOARD_ROMSIZE_KB_2048
++ select ENABLE_APIC_EXT_ID
++ select MMCONF_SUPPORT_DEFAULT
++ select DRIVERS_I2C_W83795
++ select DRIVERS_ASPEED_AST2050
++ select MAINBOARD_HAS_NATIVE_VGA_INIT_TEXTMODECFG
++
++config MAINBOARD_DIR
++ string
++ default asus/kgpe-d16
++
++config BOOTBLOCK_MAINBOARD_INIT
++ string
++ default "mainboard/asus/kgpe-d16/bootblock.c"
++
++config DCACHE_RAM_BASE
++ hex
++ default 0xc2000
++
++config DCACHE_RAM_SIZE
++ hex
++ default 0x1e000
++
++config APIC_ID_OFFSET
++ hex
++ default 0
++
++config MAINBOARD_PART_NUMBER
++ string
++ default "KGPE-D16"
++
++config HW_MEM_HOLE_SIZEK
++ hex
++ default 0x100000
++
++config PCI_64BIT_PREF_MEM
++ bool
++ default n
++
++config MAX_CPUS
++ int
++ default 32
++
++# 2 (internal) processors per G34 socket
++config MAX_PHYSICAL_CPUS
++ int
++ default 4
++
++config SB_HT_CHAIN_ON_BUS0
++ int
++ default 1
++
++config HT_CHAIN_UNITID_BASE
++ hex
++ default 0x0
++
++config HT_CHAIN_END_UNITID_BASE
++ hex
++ default 0x20
++
++config IRQ_SLOT_COUNT
++ int
++ default 13
++
++config ONBOARD_VGA_IS_PRIMARY
++ bool
++ default y
++
++config MAINBOARD_POWER_ON_AFTER_POWER_FAIL
++ bool
++ default y
++
++endif # BOARD_ASUS_KGPE_D16
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig.name b/src/mainboard/asus/kgpe-d16/Kconfig.name
+new file mode 100644
+index 0000000..bdfa31a
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/Kconfig.name
+@@ -0,0 +1,2 @@
++config BOARD_ASUS_KGPE_D16
++ bool "KGPE-D16"
+diff --git a/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl b/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
+new file mode 100644
+index 0000000..b3c65ca
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
+@@ -0,0 +1,367 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2009 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * WARNING: Sleep/Wake is a work in progress and is still somewhat flaky!
++ */
++
++ /* Port 80 POST card debug */
++ OperationRegion (DBG0, SystemIO, 0x80, One)
++ Field (DBG0, ByteAcc, NoLock, Preserve) {
++ DBG8, 8
++ }
++
++ /* SuperIO control port */
++ Name (SPIO, 0x2E)
++
++ /* SuperIO control map */
++ OperationRegion (SPIM, SystemIO, SPIO, 0x02)
++ Field (SPIM, ByteAcc, NoLock, Preserve) {
++ INDX, 8,
++ DATA, 8
++ }
++
++ /* SuperIO control registers */
++ IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {
++ Offset (0x07),
++ CR07, 8, /* Logical device number */
++ Offset (0x2C),
++ CR2C, 8, /* GPIO3 multiplexed pin selection */
++ Offset (0x30),
++ CR30, 8, /* Logical device activation control register */
++ Offset (0xE0),
++ CRE0, 8, /* Wake control register */
++ Offset (0xE6),
++ CRE6, 8, /* Mouse wake event configuration register */
++ Offset (0xF1),
++ CRF1, 8, /* GPIO3 data register */
++ Offset (0xF3),
++ CRF3, 8, /* SUSLED mode register */
++ Offset (0xF6),
++ CRF6, 8, /* SMI/PME event generation control register */
++ Offset (0xF9),
++ CRF9, 8, /* ACPI PME configuration register */
++ }
++
++ /* Power Management I/O registers */
++ OperationRegion(PIOR, SystemIO, 0x00000CD6, 0x00000002)
++ Field(PIOR, ByteAcc, NoLock, Preserve) {
++ PIOI, 0x00000008,
++ PIOD, 0x00000008,
++ }
++ IndexField (PIOI, PIOD, ByteAcc, NoLock, Preserve) {
++ Offset(0x00), /* MiscControl */
++ , 1,
++ T1EE, 1,
++ T2EE, 1,
++ Offset(0x01), /* MiscStatus */
++ , 1,
++ T1E, 1,
++ T2E, 1,
++ Offset(0x04), /* SmiWakeUpEventEnable3 */
++ , 7,
++ SSEN, 1,
++ Offset(0x07), /* SmiWakeUpEventStatus3 */
++ , 7,
++ CSSM, 1,
++ Offset(0x10), /* AcpiEnable */
++ , 6,
++ PWDE, 1,
++ Offset(0x1C), /* ProgramIoEnable */
++ , 3,
++ MKME, 1,
++ IO3E, 1,
++ IO2E, 1,
++ IO1E, 1,
++ IO0E, 1,
++ Offset(0x1D), /* IOMonitorStatus */
++ , 3,
++ MKMS, 1,
++ IO3S, 1,
++ IO2S, 1,
++ IO1S, 1,
++ IO0S,1,
++ Offset(0x20), /* AcpiPmEvtBlk */
++ APEB, 16,
++ Offset(0x36), /* GEvtLevelConfig */
++ , 6,
++ ELC6, 1,
++ ELC7, 1,
++ Offset(0x37), /* GPMLevelConfig0 */
++ , 3,
++ PLC0, 1,
++ PLC1, 1,
++ PLC2, 1,
++ PLC3, 1,
++ PLC8, 1,
++ Offset(0x38), /* GPMLevelConfig1 */
++ , 1,
++ PLC4, 1,
++ PLC5, 1,
++ , 1,
++ PLC6, 1,
++ PLC7, 1,
++ Offset(0x3B), /* PMEStatus1 */
++ GP0S, 1,
++ GM4S, 1,
++ GM5S, 1,
++ APS, 1,
++ GM6S, 1,
++ GM7S, 1,
++ GP2S, 1,
++ STSS, 1,
++ Offset(0x55), /* SoftPciRst */
++ SPRE, 1,
++ , 1,
++ , 1,
++ PNAT, 1,
++ PWMK, 1,
++ PWNS, 1,
++
++ /* Offset(0x61), */ /* Options_1 */
++ /* ,7, */
++ /* R617,1, */
++
++ Offset(0x65), /* UsbPMControl */
++ , 4,
++ URRE, 1,
++ Offset(0x68), /* MiscEnable68 */
++ , 3,
++ TMTE, 1,
++ , 1,
++ Offset(0x7C), /* MiscEnable7C */
++ , 2,
++ BLNK, 2,
++ Offset(0x92), /* GEVENTIN */
++ , 7,
++ E7IS, 1,
++ Offset(0x96), /* GPM98IN */
++ G8IS, 1,
++ G9IS, 1,
++ Offset(0x9A), /* EnhanceControl */
++ ,7,
++ HPDE, 1,
++ Offset(0xA8), /* PIO7654Enable */
++ IO4E, 1,
++ IO5E, 1,
++ IO6E, 1,
++ IO7E, 1,
++ Offset(0xA9), /* PIO7654Status */
++ IO4S, 1,
++ IO5S, 1,
++ IO6S, 1,
++ IO7S, 1,
++ }
++
++ /* PM1 Event Block
++ * First word is PM1_Status, Second word is PM1_Enable
++ */
++ OperationRegion(P1EB, SystemIO, APEB, 0x04)
++ Field(P1EB, ByteAcc, NoLock, Preserve) {
++ TMST, 1,
++ , 3,
++ BMST, 1,
++ GBST, 1,
++ Offset(0x01),
++ PBST, 1,
++ , 1,
++ RTST, 1,
++ , 3,
++ PWST, 1,
++ SPWS, 1,
++ Offset(0x02),
++ TMEN, 1,
++ , 4,
++ GBEN, 1,
++ Offset(0x03),
++ PBEN, 1,
++ , 1,
++ RTEN, 1,
++ , 3,
++ PWDA, 1,
++ }
++
++ /* Wake status package */
++ Name(WKST,Package(){Zero, Zero})
++
++ /*
++ * \_WAK System Wake method
++ *
++ * Entry:
++ * Arg0=The value of the sleeping state S1=1, S2=2
++ *
++ * Exit:
++ * Return package of 2 DWords
++ * Dword 1 - Status
++ * 0x00000000 wake succeeded
++ * 0x00000001 Wake was signaled but failed due to lack of power
++ * 0x00000002 Wake was signaled but failed due to thermal condition
++ * Dword 2 - Power Supply state
++ * if non-zero the effective S-state the power supply entered
++ */
++ Method(\_WAK, 1) {
++ Store (0x20, DBG8)
++
++ /* Set up LEDs */
++ /* Set power LED to steady on */
++ Store(0x3, BLNK)
++
++ /* Configure SuperIO for wake */
++ /* Access SuperIO ACPI device */
++ Store(0x87, INDX)
++ Store(0x87, INDX)
++ Store(0x0A, CR07)
++
++ if (LEqual(Arg0, One)) /* Resuming from power state S1 */
++ {
++ /* Deactivate the ACPI device */
++ Store(Zero, CR30)
++
++ /* Disable PS/2 SMI/PME events */
++ And(CRF6, 0xCF, CRF6)
++ }
++ if (Lor(LEqual(Arg0, 0x03), LEqual(Arg0, 0x04))) /* Resuming from power state S3 or S4 */
++ {
++ /* Disable PS/2 wake */
++ And(CRE0, 0x1D, CRE0)
++ And(CRE6, 0x7F, CRE6)
++ }
++
++ /* Restore default SuperIO access */
++ Store(0xAA, INDX)
++
++ Store (0x21, DBG8)
++
++ /* Re-enable HPET */
++ Store(1, HPDE)
++
++ /* Restore PCIRST# so it resets USB */
++ if (LEqual(Arg0, 3)){
++ Store(1, URRE)
++ }
++
++ /* Configure southbridge for wake */
++ /* Arbitrarily clear PciExpWakeStatus */
++ Store(PWST, PWST)
++
++ Store (0x22, DBG8)
++
++ Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++
++ Return(WKST)
++ }
++
++ /*
++ * \_PTS - Prepare to Sleep method
++ *
++ * Entry:
++ * Arg0=The value of the sleeping state S1=1, S2=2, etc
++ *
++ * Exit:
++ * -none-
++ *
++ * The _PTS control method is executed at the beginning of the sleep process
++ * for S1-S5. The sleeping value is passed to the _PTS control method. This
++ * control method may be executed a relatively long time before entering the
++ * sleep state and the OS may abort the operation without notification to
++ * the ACPI driver. This method cannot modify the configuration or power
++ * state of any device in the system.
++ */
++ Method(\_PTS, 1) {
++ Store (Arg0, DBG8)
++
++ /* Set up LEDs */
++ if (LEqual(Arg0, One)) /* Power state S1 requested */
++ {
++ /* Set suspend LED to 0.25Hz toggle pulse with 50% duty cycle */
++ Store(0x2, BLNK)
++ }
++ if (LEqual(Arg0, 0x3)) /* Power state S3 requested */
++ {
++ /* Set suspend LED to 0.25Hz toggle pulse with 25% duty cycle */
++ Store(0x1, BLNK)
++ }
++
++ /* Configure SuperIO for sleep */
++ /* Access SuperIO ACPI device */
++ Store(0x87, INDX)
++ Store(0x87, INDX)
++ Store(0x0A, CR07)
++
++ /* Disable PS/2 wakeup and connect PANSW_IN to PANSW_OUT */
++ And(CRE0, 0x1F, CRE0)
++
++ if (LEqual(Arg0, One)) /* Power state S1 requested */
++ {
++ /* Activate the ACPI device */
++ Store(One, CR30)
++
++ /* Disable SMI/PME events for:
++ * LPT
++ * FDC
++ * UART
++ Store(0x00, CRF6)
++
++ /* Enable PS/2 keyboard SMI/PME events */
++ Or(CRF6, 0x10, CRF6)
++
++ /* Enable PS/2 keyboard wake */
++ Or(CRE0, 0x40, CRE0)
++
++ /* Enable PS/2 mouse SMI/PME events */
++ Or(CRF6, 0x20, CRF6)
++
++ /* Enable PS/2 mouse wake */
++ Or(CRE0, 0x20, CRE0)
++ } else {
++ /* Enable PS/2 keyboard wake on any keypress */
++ Or(CRE0, 0x41, CRE0)
++
++ /* Enable PS/2 mouse wake on any click */
++ Or(CRE0, 0x22, CRE0)
++ Or(CRE6, 0x80, CRE6)
++ }
++
++ /* Restore default SuperIO access */
++ Store(0xAA, INDX)
++
++ Store (0x10, DBG8)
++
++ /* Don't allow PCIRST# to reset USB */
++ if (LEqual(Arg0, 3)){
++ Store(0, URRE)
++ }
++
++ /* Configure southbridge for sleep */
++ /* Clear sleep SMI status flag and enable sleep SMI trap. */
++ // Store(One, CSSM) /* Set ExtEvent0 as SMI# source */
++ // Store(One, SSEN) /* Enable wake on external event 0 */
++
++ /* On older chips, clear PciExpWakeDisEn */
++ // if (LLessEqual(SBRI, 0x13)) {
++ // Store(0, PWDE)
++ // }
++
++ Store (0x11, DBG8)
++
++ /* Clear wake status structure. */
++ Store(0, Index(WKST,0))
++ Store(0, Index(WKST,1))
++ }
+\ No newline at end of file
+diff --git a/src/mainboard/asus/kgpe-d16/acpi_tables.c b/src/mainboard/asus/kgpe-d16/acpi_tables.c
+new file mode 100644
+index 0000000..4e98dfe
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/acpi_tables.c
+@@ -0,0 +1,75 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <console/console.h>
++#include <string.h>
++#include <arch/acpi.h>
++#include <arch/ioapic.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <cpu/x86/msr.h>
++#include <cpu/amd/mtrr.h>
++#include <cpu/amd/amdfam10_sysconf.h>
++
++#include "mb_sysconf.h"
++
++unsigned long acpi_fill_madt(unsigned long current)
++{
++ device_t dev;
++ u32 dword;
++ u32 gsi_base=0;
++ uint32_t apicid_sp5100;
++ uint32_t apicid_sr5650;
++ /* create all subtables for processors */
++ current = acpi_create_madt_lapics(current);
++
++ apicid_sp5100 = 0x20;
++ apicid_sr5650 = apicid_sp5100 + 1;
++
++ /* Write SB700 IOAPIC, only one */
++ current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *) current, apicid_sp5100,
++ IO_APIC_ADDR, gsi_base);
++ /* IOAPIC on rs5690 */
++ gsi_base += 24; /* SB700 has 24 IOAPIC entries. */
++ dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ if (dev) {
++ pci_write_config32(dev, 0xF8, 0x1);
++ dword = pci_read_config32(dev, 0xFC) & 0xfffffff0;
++ current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *) current, apicid_sr5650,
++ dword, gsi_base);
++ }
++
++ /* bus, source, gsirq, flags */
++ current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)
++ current, 0, 0, 2, 0);
++ current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)
++ current, 0, 9, 9, 0xF);
++ /* 0: mean bus 0--->ISA */
++ /* 0: PIC 0 */
++ /* 2: APIC 2 */
++ /* 5 mean: 0101 --> Edge-triggered, Active high */
++
++ /* create all subtables for processors */
++ current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current, 0, 5, 1);
++ current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current, 1, 5, 1);
++ /* 1: LINT1 connect to NMI */
++
++ return current;
++}
+diff --git a/src/mainboard/asus/kgpe-d16/board_info.txt b/src/mainboard/asus/kgpe-d16/board_info.txt
+new file mode 100644
+index 0000000..788888e
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/board_info.txt
+@@ -0,0 +1,5 @@
++Category: server
++ROM package: PLCC-32
++ROM protocol: LPC
++ROM socketed: y
++Flashrom support: y
+\ No newline at end of file
+diff --git a/src/mainboard/asus/kgpe-d16/bootblock.c b/src/mainboard/asus/kgpe-d16/bootblock.c
+new file mode 100644
+index 0000000..2e9d70f
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/bootblock.c
+@@ -0,0 +1,52 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2014 Edward O'Callaghan <eocallaghan@alterapraxis.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <pc80/mc146818rtc.h>
++
++void bootblock_mainboard_init(void)
++{
++ uint8_t recovery_enabled;
++ unsigned char addr;
++ unsigned char byte;
++
++ bootblock_northbridge_init();
++ bootblock_southbridge_init();
++
++ /* Recovery jumper is connected to SP5100 GPIO61, and clears the GPIO when placed in the Recovery position */
++ recovery_enabled = (!(pci_read_config8(PCI_DEV(0, 0x14, 0), 0x57) & 0x1));
++ if (recovery_enabled) {
++#if CONFIG_USE_OPTION_TABLE
++ /* Clear NVRAM checksum */
++ for (addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++) {
++ cmos_write(0x0, addr);
++ }
++
++ /* Set fallback boot */
++ byte = cmos_read(RTC_BOOT_BYTE);
++ byte &= 0xfc;
++ cmos_write(byte, RTC_BOOT_BYTE);
++#else
++ /* FIXME
++ * Figure out how to recover if the option table is not available
++ */
++#endif
++ }
++}
+\ No newline at end of file
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+new file mode 100644
+index 0000000..cffdd03
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -0,0 +1,16 @@
++baud_rate = 115200
++debug_level = Spew
++multi_core = Enable
++slow_cpu = off
++iommu = Disable
++nmi = Disable
++hypertransport_speed_limit = Auto
++max_mem_clock = DDR3-1600
++ECC_memory = Enable
++ECC_redirection = Disable
++ecc_scrub_rate = 1.28us
++interleave_chip_selects = Enable
++interleave_nodes = Disable
++interleave_memory_channels = Enable
++power_on_after_fail = On
++boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+new file mode 100644
+index 0000000..bcf9cd3
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -0,0 +1,134 @@
++##
++## This file is part of the coreboot project.
++##
++## Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++## Copyright (C) 2007 AMD
++## Written by Yinghai Lu <yinghailu@amd.com> for AMD.
++##
++## This program is free software; you can redistribute it and/or modify
++## it under the terms of the GNU General Public License as published by
++## the Free Software Foundation; either version 2 of the License, or
++## (at your option) any later version.
++##
++## This program is distributed in the hope that it will be useful,
++## but WITHOUT ANY WARRANTY; without even the implied warranty of
++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++## GNU General Public License for more details.
++##
++## You should have received a copy of the GNU General Public License
++## along with this program; if not, write to the Free Software
++## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++##
++
++entries
++
++0 384 r 0 reserved_memory
++384 1 e 4 boot_option
++385 1 e 4 last_boot
++388 4 r 0 reboot_bits
++393 3 e 5 baud_rate
++396 5 e 10 ecc_scrub_rate
++401 1 e 1 interleave_chip_selects
++402 1 e 1 interleave_nodes
++403 1 e 1 interleave_memory_channels
++404 2 e 8 max_mem_clock
++406 1 e 2 multi_core
++412 4 e 6 debug_level
++440 4 e 9 slow_cpu
++444 1 e 1 nmi
++445 1 e 1 iommu
++446 2 e 3 power_on_after_fail
++456 1 e 1 ECC_memory
++457 1 e 1 ECC_redirection
++458 4 e 11 hypertransport_speed_limit
++728 256 h 0 user_data
++984 16 h 0 check_sum
++# Reserve the extended AMD configuration registers
++1000 24 r 0 amd_reserved
++
++
++
++enumerations
++
++#ID value text
++1 0 Disable
++1 1 Enable
++2 0 Enable
++2 1 Disable
++3 0 Off
++3 1 On
++3 2 Last
++4 0 Fallback
++4 1 Normal
++5 0 115200
++5 1 57600
++5 2 38400
++5 3 19200
++5 4 9600
++5 5 4800
++5 6 2400
++5 7 1200
++6 0 Emergency
++6 1 Alert
++6 2 Critical
++6 3 Error
++6 4 Warning
++6 5 Notice
++6 6 Information
++6 7 Debug
++6 8 Spew
++8 0 DDR3-1600
++8 1 DDR3-1333
++8 2 DDR3-1066
++8 3 DDR3-800
++9 0 off
++9 1 87.5%
++9 2 75.0%
++9 3 62.5%
++9 4 50.0%
++9 5 37.5%
++9 6 25.0%
++9 7 12.5%
++10 0 Disabled
++10 1 40ns
++10 2 80ns
++10 3 160ns
++10 4 320ns
++10 5 640ns
++10 6 1.28us
++10 7 2.56us
++10 8 5.12us
++10 9 10.2us
++10 10 20.5us
++10 11 41us
++10 12 81.9us
++10 13 163.8us
++10 14 327.7us
++10 15 655.4us
++10 16 1.31ms
++10 17 2.62ms
++10 18 5.24ms
++10 19 10.49ms
++10 20 20.97ms
++10 21 42ms
++10 22 84ms
++11 0 Auto
++11 1 2.6GHz
++11 2 2.4GHz
++11 3 2.2GHz
++11 4 2.0GHz
++11 5 1.8GHz
++11 6 1.6GHz
++11 7 1.4GHz
++11 8 1.2GHz
++11 9 1.0GHz
++11 10 800MHz
++11 11 600MHz
++11 12 500MHz
++11 13 400MHz
++11 14 300MHz
++11 15 200MHz
++
++checksums
++
++checksum 392 983 984
+diff --git a/src/mainboard/asus/kgpe-d16/devicetree.cb b/src/mainboard/asus/kgpe-d16/devicetree.cb
+new file mode 100644
+index 0000000..d0288da
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/devicetree.cb
+@@ -0,0 +1,248 @@
++chip northbridge/amd/amdfam10/root_complex # Root complex
++ device cpu_cluster 0 on # (L)APIC cluster
++ chip cpu/amd/socket_F_1207 # CPU socket
++ device lapic 0 on end # Local APIC of the CPU
++ end
++ end
++ device domain 0 on # PCI domain
++ subsystemid 0x1043 0x8163 inherit
++ chip northbridge/amd/amdfam10 # Northbridge / RAM controller
++ register "maximum_memory_capacity" = "0x4000000000" # 256GB
++ device pci 18.0 on end # Link 0 == LDT 0
++ device pci 18.0 on end # Link 1 == LDT 1
++ device pci 18.0 on end # Link 2 == LDT 2
++ device pci 18.0 on # Link 3 == LDT 3 [SB on link 3]
++ chip southbridge/amd/sr5650 # Primary southbridge
++ device pci 0.0 on end # HT Root Complex 0x9600
++ device pci 0.1 on end # CLKCONFIG
++ device pci 2.0 on # PCIE P2P bridge 0x9603 (GPP1 Port0)
++ # Slot # PCI E 1 / PCI E 2
++ end
++ device pci 3.0 off end # PCIE P2P bridge 0x960b (GPP1 Port1)
++ device pci 4.0 on # PCIE P2P bridge 0x9604 (GPP3a Port0)
++ # PIKE SAS
++ end
++ device pci 5.0 off end # PCIE P2P bridge 0x9605 (GPP3a Port1)
++ device pci 6.0 off end # PCIE P2P bridge 0x9606 (GPP3a Port2)
++ device pci 7.0 off end # PCIE P2P bridge 0x9607 (GPP3a Port3)
++ device pci 8.0 off end # NB/SB Link P2P bridge
++ device pci 9.0 on # Bridge (GPP3a Port4)
++ # Onboard # NIC A
++ end
++ device pci a.0 on # Bridge (GPP3a Port5)
++ # Onboard # NIC B
++ end
++ device pci b.0 on # Bridge (GPP2 Port0)
++ # Slot # PCI E 4
++ end
++ device pci c.0 on # Bridge (GPP2 Port1)
++ # Slot # PCI E 5
++ end
++ device pci d.0 on # Bridge (GPP3b Port0)
++ # Slot # PCI E 3
++ end
++ register "gpp1_configuration" = "0" # Configuration 16:0 default
++ register "gpp2_configuration" = "1" # Configuration 8:8
++ #register "gpp3a_configuration" = "2" # Configuration 4:1:1:0:0:0
++ register "gpp3a_configuration" = "11" # Configuration 1:1:1:1:1:1
++ register "port_enable" = "0x3ffc" # Enable all ports except 0 and 1
++ end
++ chip southbridge/amd/sb700 # Secondary southbridge
++ device pci 11.0 on end # SATA
++ device pci 12.0 on end # USB
++ device pci 12.1 on end # USB
++ device pci 12.2 on end # USB
++ device pci 13.0 on end # USB
++ device pci 13.1 on end # USB
++ device pci 13.2 on end # USB
++ device pci 14.0 on end # SM
++ device pci 14.1 on end # IDE 0x439c
++ device pci 14.2 off end # HDA 0x4383 (KGPE-D16 omits audio option)
++ device pci 14.3 on # LPC 0x439d
++ chip superio/nuvoton/nct5572d # Super I/O
++ device pnp 2e.0 off end # FDC; Not available on the KGPE-D16
++ device pnp 2e.1 off end # LPT1; Not available on the KGPE-D16
++ device pnp 2e.2 on # Com1
++ io 0x60 = 0x3f8
++ irq 0x70 = 4
++ end
++ device pnp 2e.3 off # IR: Not available on the KGPE-D16
++ io 0x60 = 0x2f8
++ irq 0x70 = 3
++ end
++ device pnp 2e.5 on # PS/2 keyboard & mouse
++ io 0x60 = 0x60
++ io 0x62 = 0x64
++ irq 0x70 = 1
++ irq 0x72 = 12
++ end
++ device pnp 2e.6 off # CIR: Not available on the KGPE-D16
++ io 0x60 = 0x100
++ irq 0x70 = 0
++ end
++ device pnp 2e.7 off end # GIPO689
++ device pnp 2e.8 off end # WDT
++ device pnp 2e.9 off end # GPIO235
++ device pnp 2e.a on end # ACPI
++ device pnp 2e.b on # HW Monitor
++ io 0x60 = 0x290
++ io 0x62 = 0x0000 # SB-TSI currently not implemented
++ irq 0x70 = 5
++ end
++ device pnp 2e.c off end # PECI
++ device pnp 2e.d off end # SUSLED
++ device pnp 2e.e off # CIRWKUP
++ io 0x60 = 0x0000
++ irq 0x70 = 0
++ end
++ device pnp 2e.f off end # GPIO_PP_OD
++ end
++ chip drivers/generic/generic # DIMM n-0-0-0
++ device i2c 50 on end
++ end
++ chip drivers/generic/generic # DIMM n-0-0-1
++ device i2c 51 on end
++ end
++ chip drivers/generic/generic # DIMM n-0-1-0
++ device i2c 52 on end
++ end
++ chip drivers/generic/generic # DIMM n-0-1-1
++ device i2c 53 on end
++ end
++ chip drivers/generic/generic # DIMM n-1-0-0
++ device i2c 54 on end
++ end
++ chip drivers/generic/generic # DIMM n-1-0-1
++ device i2c 55 on end
++ end
++ chip drivers/generic/generic # DIMM n-1-1-0
++ device i2c 56 on end
++ end
++ chip drivers/generic/generic # DIMM n-1-1-1
++ device i2c 57 on end
++ end
++ chip drivers/i2c/w83795
++ register "fanin_ctl1" = "0xff" # Enable monitoring of FANIN1 - FANIN8
++ register "fanin_ctl2" = "0x00" # Connect FANIN11 - FANIN14 to alternate functions
++ register "temp_ctl1" = "0x2a" # Enable monitoring of DTS, VSEN12, and VSEN13
++ register "temp_ctl2" = "0x01" # Enable monitoring of TD1/TR1
++ register "temp_dtse" = "0x03" # Enable DTS1 and DTS2
++ register "volt_ctl1" = "0xff" # Enable monitoring of VSEN1 - VSEN8
++ register "volt_ctl2" = "0xf7" # Enable monitoring of VSEN9 - VSEN11, 3VDD, 3VSB, and VBAT
++ register "temp1_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp1)
++ register "temp2_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp2)
++ register "temp3_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp3)
++ register "temp4_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp4)
++ register "temp5_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp5)
++ register "temp6_fan_select" = "0x00" # All fans to manual mode (no dependence on Temp6)
++ register "temp1_source_select" = "0x00" # Use TD1/TR1 as data source for Temp1
++ register "temp2_source_select" = "0x00" # Use TD2/TR2 as data source for Temp2
++ register "temp3_source_select" = "0x00" # Use TD3/TR3 as data source for Temp3
++ register "temp4_source_select" = "0x00" # Use TD4/TR4 as data source for Temp4
++ register "temp5_source_select" = "0x00" # Use TR5 as data source for Temp5
++ register "temp6_source_select" = "0x00" # Use TR6 as data source for Temp6
++ register "tr1_critical_temperature" = "85" # Set TD1/TR1 critical temperature to 85°C
++ register "tr1_critical_hysteresis" = "80" # Set TD1/TR1 critical hysteresis temperature to 80°C
++ register "tr1_warning_temperature" = "70" # Set TD1/TR1 warning temperature to 70°C
++ register "tr1_warning_hysteresis" = "65" # Set TD1/TR1 warning hysteresis temperature to 65°C
++ register "dts_critical_temperature" = "85" # Set DTS (CPU) critical temperature to 85°C
++ register "dts_critical_hysteresis" = "80" # Set DTS (CPU) critical hysteresis temperature to 80°C
++ register "dts_warning_temperature" = "70" # Set DTS (CPU) warning temperature to 70°C
++ register "dts_warning_hysteresis" = "65" # Set DTS (CPU) warning hysteresis temperature to 65°C
++ register "temp1_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp2_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp3_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp4_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp5_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp6_critical_temperature" = "80" # Set Temp1 critical temperature to 80°C
++ register "temp1_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "temp2_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "temp3_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "temp4_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "temp5_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "temp6_target_temperature" = "80" # Set Temp1 target temperature to 80°C
++ register "fan1_nonstop" = "7" # Set Fan 1 minimum speed
++ register "fan2_nonstop" = "7" # Set Fan 2 minimum speed
++ register "fan3_nonstop" = "7" # Set Fan 3 minimum speed
++ register "fan4_nonstop" = "7" # Set Fan 4 minimum speed
++ register "fan5_nonstop" = "7" # Set Fan 5 minimum speed
++ register "fan6_nonstop" = "7" # Set Fan 6 minimum speed
++ register "fan7_nonstop" = "7" # Set Fan 7 minimum speed
++ register "fan8_nonstop" = "7" # Set Fan 8 minimum speed
++ register "default_speed" = "100" # All fans to full speed on power up
++ register "fan1_duty" = "100" # Fan 1 to full speed
++ register "fan2_duty" = "100" # Fan 2 to full speed
++ register "fan3_duty" = "100" # Fan 3 to full speed
++ register "fan4_duty" = "100" # Fan 4 to full speed
++ register "fan5_duty" = "100" # Fan 5 to full speed
++ register "fan6_duty" = "100" # Fan 6 to full speed
++ register "fan7_duty" = "100" # Fan 7 to full speed
++ register "fan8_duty" = "100" # Fan 8 to full speed
++ register "vcore1_high_limit_mv" = "1500" # VCORE1 (Node 0) high limit to 1.5V
++ register "vcore1_low_limit_mv" = "900" # VCORE1 (Node 0) low limit to 0.9V
++ register "vcore2_high_limit_mv" = "1500" # VCORE2 (Node 1) high limit to 1.5V
++ register "vcore2_low_limit_mv" = "900" # VCORE2 (Node 1) low limit to 0.9V
++ register "vsen3_high_limit_mv" = "1600" # VSEN1 (Node 0 RAM voltage) high limit to 1.6V
++ register "vsen3_low_limit_mv" = "1100" # VSEN1 (Node 0 RAM voltage) low limit to 1.1V
++ register "vsen4_high_limit_mv" = "1600" # VSEN2 (Node 1 RAM voltage) high limit to 1.6V
++ register "vsen4_low_limit_mv" = "1100" # VSEN2 (Node 1 RAM voltage) low limit to 1.1V
++ register "vsen5_high_limit_mv" = "1250" # VSEN5 (Node 0 HT link voltage) high limit to 1.25V
++ register "vsen5_low_limit_mv" = "1150" # VSEN5 (Node 0 HT link voltage) low limit to 1.15V
++ register "vsen6_high_limit_mv" = "1250" # VSEN6 (Node 1 HT link voltage) high limit to 1.25V
++ register "vsen6_low_limit_mv" = "1150" # VSEN6 (Node 1 HT link voltage) low limit to 1.15V
++ register "vsen7_high_limit_mv" = "1150" # VSEN7 (Northbridge core voltage) high limit to 1.15V
++ register "vsen7_low_limit_mv" = "1050" # VSEN7 (Northbridge core voltage) low limit to 1.05V
++ register "vsen8_high_limit_mv" = "1900" # VSEN8 (+1.8V) high limit to 1.9V
++ register "vsen8_low_limit_mv" = "1700" # VSEN8 (+1.8V) low limit to 1.7V
++ register "vsen9_high_limit_mv" = "1250" # VSEN9 (+1.2V) high limit to 1.25V
++ register "vsen9_low_limit_mv" = "1150" # VSEN9 (+1.2V) low limit to 1.15V
++ register "vsen10_high_limit_mv" = "1150" # VSEN10 (+1.1V) high limit to 1.15V
++ register "vsen10_low_limit_mv" = "1050" # VSEN10 (+1.1V) low limit to 1.05V
++ register "vsen11_high_limit_mv" = "1625" # VSEN11 (5VSB, scaling factor ~3.2) high limit to 5.2V
++ register "vsen11_low_limit_mv" = "1500" # VSEN11 (5VSB, scaling factor ~3.2) low limit to 4.8V
++ register "vsen12_high_limit_mv" = "1083" # VSEN12 (+12V, scaling factor ~12) high limit to 13V
++ register "vsen12_low_limit_mv" = "917" # VSEN12 (+12V, scaling factor ~12) low limit to 11V
++ register "vsen13_high_limit_mv" = "1625" # VSEN13 (+5V, scaling factor ~3.2) high limit to 5.2V
++ register "vsen13_low_limit_mv" = "1500" # VSEN13 (+5V, scaling factor ~3.2) low limit to 4.8V
++ register "vdd_high_limit_mv" = "3500" # 3VDD high limit to 3.5V
++ register "vdd_low_limit_mv" = "3100" # 3VDD low limit to 3.1V
++ register "vsb_high_limit_mv" = "3500" # 3VSB high limit to 3.5V
++ register "vsb_low_limit_mv" = "3100" # 3VSB low limit to 3.1V
++ register "vbat_high_limit_mv" = "3500" # VBAT (+3V) high limit to 3.5V
++ register "vbat_low_limit_mv" = "2500" # VBAT (+3V) low limit to 2.5V
++ register "smbus_aux" = "1" # Device located on auxiliary SMBUS
++ device i2c 0x2f on end
++ end
++ end
++ device pci 14.4 on # Bridge
++ device pci 1.0 on end # VGA
++ device pci 2.0 on end # FireWire
++ device pci 3.0 on # Slot
++ # Slot # PCI 0
++ end
++ end
++ device pci 14.5 on end # USB OHCI2 0x4399
++ end
++ end
++ device pci 18.1 on end
++ device pci 18.2 on end
++ device pci 18.3 on end
++ device pci 18.4 on end
++ device pci 19.0 on end # Socket 0 node 1
++ device pci 19.1 on end
++ device pci 19.2 on end
++ device pci 19.3 on end
++ device pci 19.4 on end
++ device pci 1a.0 on end # Socket 1 node 0
++ device pci 1a.1 on end
++ device pci 1a.2 on end
++ device pci 1a.3 on end
++ device pci 1a.4 on end
++ device pci 1b.0 on end # Socket 1 node 1
++ device pci 1b.1 on end
++ device pci 1b.2 on end
++ device pci 1b.3 on end
++ device pci 1b.4 on end
++ end
++ end
++end
+diff --git a/src/mainboard/asus/kgpe-d16/dsdt.asl b/src/mainboard/asus/kgpe-d16/dsdt.asl
+new file mode 100644
+index 0000000..bdd1d2d
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/dsdt.asl
+@@ -0,0 +1,730 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2005 - 2012 Advanced Micro Devices, Inc.
++ * Copyright (C) 2007-2009 coresystems GmbH
++ * Copyright (C) 2004 Nick Barker <Nick.Barker9@btinternet.com>
++ * Copyright (C) 2007, 2008 Rudolf Marek <r.marek@assembler.cz>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * WARNING: Sleep/Wake is a work in progress and is still somewhat flaky!
++ * Everything else does to the best of my knowledge... (T.P. 01/26/2015)
++ */
++
++/*
++ * ISA portions taken from QEMU acpi-dsdt.dsl.
++ */
++
++/*
++ * PCI link routing templates taken from ck804.asl and modified for this board
++ */
++
++DefinitionBlock (
++ "DSDT.AML", /* Output filename */
++ "DSDT", /* Signature */
++ 0x02, /* DSDT Revision, needs to be 2 for 64bit */
++ "ASUS ", /* OEMID */
++ "COREBOOT", /* TABLE ID */
++ 0x00000001 /* OEM Revision */
++ )
++{
++ #include "northbridge/amd/amdfam10/amdfam10_util.asl"
++ #include "southbridge/amd/sr5650/acpi/sr5650.asl"
++
++ /* Some global data */
++ Name(OSVR, 3) /* Assume nothing. WinXp = 1, Vista = 2, Linux = 3, WinCE = 4 */
++ Name(OSV, Ones) /* Assume nothing */
++ Name(PICM, One) /* Assume APIC */
++
++ /* HPET control */
++ Name (SHPB, 0xFED00000)
++ Name (SHPL, 0x1000)
++
++ /* Define power states */
++ Name (\_S0, Package () { 0x00, 0x00, 0x00, 0x00 }) /* Normal operation */
++ Name (\_S1, Package () { 0x01, 0x01, 0x00, 0x00 }) /* Standby */
++ Name (\_S2, Package () { 0x02, 0x02, 0x00, 0x00 }) /* Standby w/ CPU shutdown */
++ Name (\_S3, Package () { 0x03, 0x00, 0x00, 0x00 }) /* Suspend */
++ /* Name (\_S4, Package () { 0x04, 0x04, 0x00, 0x00 }) */
++ Name (\_S5, Package () { 0x05, 0x05, 0x00, 0x00 }) /* Hard power off */
++
++ /* The _PIC method is called by the OS to choose between interrupt
++ * routing via the i8259 interrupt controller or the APIC.
++ *
++ * _PIC is called with a parameter of 0 for i8259 configuration and
++ * with a parameter of 1 for Local Apic/IOAPIC configuration.
++ */
++ Method (_PIC, 1, Serialized) {
++ If (Arg0)
++ {
++ \_SB.CIRQ()
++ }
++ Store (Arg0, PICM)
++ }
++
++ /* _PR CPU0 is dynamically supplied by SSDT */
++ /* CPU objects and _PSS entries are dynamically supplied by SSDT */
++
++ Scope(\_GPE) { /* Start Scope GPE */
++ /* General event 3 */
++ Method(_L03) {
++ /* Level-Triggered GPE */
++ Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ }
++
++ /* General event 4 */
++ Method(_L04) {
++ /* Level-Triggered GPE */
++ Notify (\_SB.PCI0.PBR0, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ }
++
++ /* Keyboard controller PME# */
++ Method(_L08) {
++ /* Level-Triggered GPE */
++ Notify(\_SB.PCI0.LPC.KBD, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify(\_SB.PCI0.LPC.MOU, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ }
++
++ /* USB controller PME# */
++ Method(_L0B) {
++ /* Level-Triggered GPE */
++ Notify (\_SB.PCI0.USB0, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB1, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB2, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB3, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB4, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB5, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.USB6, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ }
++
++ /* GPIO0 or GEvent8 event */
++ Method(_L18) {
++ /* Level-Triggered GPE */
++ Notify (\_SB.PCI0.PCE1, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.NICA, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.NICB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.PCE4, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.PCE5, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Notify (\_SB.PCI0.PCE3, 0x02) /* NOTIFY_DEVICE_WAKE */
++ }
++
++ } /* End Scope GPE */
++
++ /* Root of the bus hierarchy */
++ Scope (\_SB)
++ {
++ /* Top southbridge PCI device (SR5690) */
++ Device (PCI0)
++ {
++ /* BUS0 root bus */
++
++ Name (_HID, EisaId ("PNP0A03"))
++ Name (_ADR, 0x00180001)
++ Name (_UID, 0x00)
++
++ Name (HCIN, 0x00) // HC1
++
++ Method (_BBN, 0, NotSerialized)
++ {
++ Return (GBUS (GHCN(HCIN), GHCL(HCIN)))
++ }
++
++ /* Operating System Capabilities Method */
++ Method(_OSC,4)
++ {
++ /* Let OS control everything */
++ Return (Arg3)
++ }
++
++ External (BUSN)
++ External (MMIO)
++ External (PCIO)
++ External (SBLK)
++ External (TOM1)
++ External (HCLK)
++ External (SBDN)
++ External (HCDN)
++ External (CBST)
++
++ /* PCI Routing Tables */
++ Name (PR00, Package () {
++ /* PIC */
++ /* Top southbridge device (SR5690) */
++ /* HT Link */
++ Package (0x04) { 0x0000FFFF, 0x00, LNKA, 0x00 },
++
++ /* PCI-E Slot 1 (Bridge) */
++ Package (0x04) { 0x0002FFFF, 0x00, LNKE, 0x00 },
++
++ /* NIC A (Bridge) */
++ Package (0x04) { 0x0009FFFF, 0x00, LNKF, 0x00 },
++
++ /* NIC B (Bridge) */
++ Package (0x04) { 0x000AFFFF, 0x00, LNKG, 0x00 },
++
++ /* PCI-E Slot 4 (Bridge) */
++ Package (0x04) { 0x000BFFFF, 0x00, LNKG, 0x00 },
++
++ /* PCI-E Slot 5 (Bridge) */
++ Package (0x04) { 0x000CFFFF, 0x00, LNKG, 0x00 },
++
++ /* PCI-E Slot 3 (Bridge) */
++ Package (0x04) { 0x000DFFFF, 0x00, LNKG, 0x00 },
++
++ /* Bottom southbridge device (SP5100) */
++ /* SATA 0 */
++ Package (0x04) { 0x0011FFFF, 0x00, LNKG, 0x00 },
++
++ /* USB 0 */
++ Package (0x04) { 0x0012FFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0x0012FFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0x0012FFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0x0012FFFF, 0x03, LNKD, 0x00 },
++
++ /* USB 1 */
++ Package (0x04) { 0x0013FFFF, 0x00, LNKC, 0x00 },
++ Package (0x04) { 0x0013FFFF, 0x01, LNKD, 0x00 },
++ Package (0x04) { 0x0013FFFF, 0x02, LNKA, 0x00 },
++ Package (0x04) { 0x0013FFFF, 0x03, LNKB, 0x00 },
++
++ /* SMBUS / IDE / LPC / VGA / FireWire / PCI Slot 0 */
++ Package (0x04) { 0x0014FFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0x0014FFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0x0014FFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0x0014FFFF, 0x03, LNKD, 0x00 },
++ })
++
++ Name (AR00, Package () {
++ /* APIC */
++ /* Top southbridge device (SR5690) */
++ /* HT Link */
++ Package (0x04) { 0x0000FFFF, 0x00, 0x00, 55 },
++
++ /* PCI-E Slot 1 (Bridge) */
++ Package (0x04) { 0x0002FFFF, 0x00, 0x00, 52 },
++
++ /* NIC A (Bridge) */
++ Package (0x04) { 0x0009FFFF, 0x00, 0x00, 53 },
++
++ /* NIC B (Bridge) */
++ Package (0x04) { 0x000AFFFF, 0x00, 0x00, 54 },
++
++ /* PCI-E Slot 4 (Bridge) */
++ Package (0x04) { 0x000BFFFF, 0x00, 0x00, 54 },
++
++ /* PCI-E Slot 5 (Bridge) */
++ Package (0x04) { 0x000CFFFF, 0x00, 0x00, 54 },
++
++ /* PCI-E Slot 3 (Bridge) */
++ Package (0x04) { 0x000DFFFF, 0x00, 0x00, 54 },
++
++ /* Bottom southbridge device (SP5100) */
++ /* SATA 0 */
++ Package (0x04) { 0x0011FFFF, 0x00, 0x00, 22 },
++
++ /* USB 0 */
++ Package (0x04) { 0x0012FFFF, 0x00, 0x00, 16 },
++ Package (0x04) { 0x0012FFFF, 0x01, 0x00, 17 },
++ Package (0x04) { 0x0012FFFF, 0x02, 0x00, 18 },
++ Package (0x04) { 0x0012FFFF, 0x03, 0x00, 19 },
++
++ /* USB 1 */
++ Package (0x04) { 0x0013FFFF, 0x00, 0x00, 18 },
++ Package (0x04) { 0x0013FFFF, 0x01, 0x00, 19 },
++ Package (0x04) { 0x0013FFFF, 0x02, 0x00, 16 },
++ Package (0x04) { 0x0013FFFF, 0x03, 0x00, 17 },
++
++ /* SMBUS / IDE / LPC / VGA / FireWire / PCI Slot 0 */
++ Package (0x04) { 0x0014FFFF, 0x00, 0x00, 16 },
++ Package (0x04) { 0x0014FFFF, 0x01, 0x00, 17 },
++ Package (0x04) { 0x0014FFFF, 0x02, 0x00, 18 },
++ Package (0x04) { 0x0014FFFF, 0x03, 0x00, 19 },
++ })
++
++ Name (PR01, Package () {
++ /* PIC */
++ Package (0x04) { 0x1FFFF, 0x00, LNKF, 0x00 },
++ Package (0x04) { 0x2FFFF, 0x00, LNKE, 0x00 },
++ Package (0x04) { 0x3FFFF, 0x00, LNKG, 0x00 },
++ Package (0x04) { 0x3FFFF, 0x01, LNKH, 0x00 },
++ Package (0x04) { 0x3FFFF, 0x02, LNKE, 0x00 },
++ Package (0x04) { 0x3FFFF, 0x03, LNKF, 0x00 },
++ })
++
++ Name (AR01, Package () {
++ /* APIC */
++ Package (0x04) { 0x1FFFF, 0x00, 0x00, 21 },
++ Package (0x04) { 0x2FFFF, 0x00, 0x00, 20 },
++ Package (0x04) { 0x3FFFF, 0x00, 0x00, 22 },
++ Package (0x04) { 0x3FFFF, 0x01, 0x00, 23 },
++ Package (0x04) { 0x3FFFF, 0x02, 0x00, 20 },
++ Package (0x04) { 0x3FFFF, 0x03, 0x00, 21 },
++ })
++
++ Name (PR02, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
++ })
++
++ Name (AR02, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 24 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 25 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 26 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 27 },
++ })
++
++ Name (PR03, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
++ })
++
++ Name (AR03, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 48 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 49 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 50 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 51 },
++ })
++
++ Name (PR04, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKH, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKE, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKF, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKG, 0x00 },
++ })
++
++ Name (AR04, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 47 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 44 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 45 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 46 },
++ })
++
++ Name (PR05, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
++ })
++
++ Name (AR05, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 32 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 33 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 34 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 35 },
++ })
++
++ Name (PR06, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKE, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKF, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKG, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKH, 0x00 },
++ })
++
++ Name (AR06, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 36 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 37 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 38 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 39 },
++ })
++
++ Name (PR07, Package () {
++ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKC, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
++ })
++
++ Name (AR07, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 40 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 41 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 42 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 43 },
++ })
++
++ /* PCI Resource Tables */
++
++ /* PCI Resource Settings Access */
++ Method (_CRS, 0, Serialized)
++ {
++ Name (BUF0, ResourceTemplate ()
++ {
++ IO (Decode16,
++ 0x0CF8, // Address Range Minimum
++ 0x0CF8, // Address Range Maximum
++ 0x01, // Address Alignment
++ 0x08, // Address Length
++ )
++ WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
++ 0x0000, // Address Space Granularity
++ 0x0000, // Address Range Minimum
++ 0x0CF7, // Address Range Maximum
++ 0x0000, // Address Translation Offset
++ 0x0CF8, // Address Length
++ ,, , TypeStatic)
++ })
++ /* Methods below use SSDT to get actual MMIO regs
++ The IO ports are from 0xd00, optionally an VGA,
++ otherwise the info from MMIO is used.
++ \_SB.GXXX(node, link)
++ */
++ Concatenate (\_SB.GMEM (0x00, \_SB.PCI0.SBLK), BUF0, Local1)
++ Concatenate (\_SB.GIOR (0x00, \_SB.PCI0.SBLK), Local1, Local2)
++ Concatenate (\_SB.GWBN (0x00, \_SB.PCI0.SBLK), Local2, Local3)
++ Return (Local3)
++ }
++
++ /* PCI Routing Table Access */
++ Method (_PRT, 0, NotSerialized) {
++ If (PICM) {
++ Return (AR00)
++ } Else {
++ Return (PR00)
++ }
++ }
++
++ /* 0:11.0 SP5100 SATA 0 */
++ Device(SAT0)
++ {
++ Name (_ADR, 0x00110000) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ #include "southbridge/amd/sb700/acpi/sata.asl"
++ }
++
++ /* 0:12.0 SP5100 USB 0 */
++ Device (USB0)
++ {
++ Name (_ADR, 0x00120000) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:12.1 SP5100 USB 1 */
++ Device (USB1)
++ {
++ Name (_ADR, 0x00120001) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:12.2 SP5100 USB 2 */
++ Device (USB2)
++ {
++ Name (_ADR, 0x00120002) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:13.0 SP5100 USB 3 */
++ Device (USB3)
++ {
++ Name (_ADR, 0x00130000) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:13.1 SP5100 USB 4 */
++ Device (USB4)
++ {
++ Name (_ADR, 0x00130001) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:13.2 SP5100 USB 5 */
++ Device (USB5)
++ {
++ Name (_ADR, 0x00130002) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 0:14.1 SP5100 IDE Controller */
++ Device (IDEC)
++ {
++ Name (_ADR, 0x00140001) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ #include "southbridge/amd/sb700/acpi/ide.asl"
++ }
++
++ /* 0:14.3 SP5100 LPC */
++ Device (LPC) {
++ Name (_HID, EisaId ("PNP0A05"))
++ Name (_ADR, 0x00140003)
++
++ /* PS/2 keyboard (seems to be important for WinXP install) */
++ Device (KBD)
++ {
++ Name (_HID, EisaId ("PNP0303"))
++ Name (_CID, EisaId ("PNP030B"))
++ Method (_STA, 0, NotSerialized)
++ {
++ Return (0x0f)
++ }
++ Method (_CRS, 0, Serialized)
++ {
++ Name (TMP, ResourceTemplate () {
++ IO (Decode16, 0x0060, 0x0060, 0x01, 0x01)
++ IO (Decode16, 0x0064, 0x0064, 0x01, 0x01)
++ IRQNoFlags () {1}
++ })
++ Return (TMP)
++ }
++ }
++
++ /* PS/2 mouse */
++ Device (MOU)
++ {
++ Name (_HID, EisaId ("PNP0F03"))
++ Name (_CID, EisaId ("PNP0F13"))
++ Method (_STA, 0, NotSerialized)
++ {
++ Return (0x0f)
++ }
++ Method (_CRS, 0, Serialized)
++ {
++ Name (TMP, ResourceTemplate () {
++ IRQNoFlags () {12}
++ })
++ Return (TMP)
++ }
++ }
++
++
++ /* UART 1 */
++ Device (URT1)
++ {
++ Name (_HID, EisaId ("PNP0501")) // "PNP0501" for UART
++ Name(_PRW, Package () {0x03, 0x04}) // Wake from S1-S4
++ Method (_STA, 0, NotSerialized)
++ {
++ Return (0x0f) // Always enable
++ }
++ Name (_PRS, ResourceTemplate() {
++ StartDependentFn(0, 1) {
++ IO(Decode16, 0x3f8, 0x3f8, 0x8, 0x8)
++ IRQNoFlags() { 4 }
++ } EndDependentFn()
++ })
++ Method (_CRS, 0)
++ {
++ Return(ResourceTemplate() {
++ IO(Decode16, 0x3f8, 0x3f8, 0x8, 0x8)
++ IRQNoFlags() { 4 }
++ })
++ }
++ }
++
++ /* High Precision Event Timer */
++ Device (HPET)
++ {
++ Name (_HID, EisaId ("PNP0103"))
++ Name (CRS, ResourceTemplate ()
++ {
++ Memory32Fixed (ReadOnly,
++ 0x00000000,
++ 0x00001000,
++ _Y02)
++ IRQNoFlags () {0}
++ IRQNoFlags () {8}
++ })
++ Method (_STA, 0, NotSerialized)
++ {
++ Return (0x0F)
++ }
++ Method (_CRS, 0, NotSerialized)
++ {
++ CreateDWordField (CRS, \_SB.PCI0.LPC.HPET._Y02._BAS, HPT1)
++ CreateDWordField (CRS, \_SB.PCI0.LPC.HPET._Y02._LEN, HPT2)
++ Store (SHPB, HPT1)
++ Store (SHPL, HPT2)
++ Return (CRS)
++ }
++
++ }
++ }
++
++ /* 0:14.4 PCI Bridge */
++ Device (PBR0)
++ {
++ Name (_ADR, 0x00140004) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR01)
++ } Else {
++ Return (PR01)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++
++ /* 0:14.5 SP5100 USB 6 */
++ Device (USB6)
++ {
++ Name (_ADR, 0x00140005) // _ADR: Address
++ Name(_PRW, Package () {0x05, 0x04}) // Wake from S1-S4
++ }
++
++ /* 2:00.0 PCIe x16 */
++ Device (PCE1)
++ {
++ Name (_ADR, 0x00020000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR02)
++ } Else {
++ Return (PR02)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++
++ /* 3:00.0 PCIe NIC A */
++ Device (NICA)
++ {
++ Name (_ADR, 0x00090000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR03)
++ } Else {
++ Return (PR03)
++ }
++ }
++ Device (BDC1)
++ {
++ Name (_ADR, Zero) // _ADR: Address
++ }
++ }
++
++ /* 4:00.0 PCIe NIC B */
++ Device (NICB)
++ {
++ Name (_ADR, 0x000A0000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR04)
++ } Else {
++ Return (PR04)
++ }
++ }
++ Device (BDC2)
++ {
++ Name (_ADR, Zero) // _ADR: Address
++ }
++ }
++
++ /* 5:00.0 PCIe x16 */
++ Device (PCE4)
++ {
++ Name (_ADR, 0x000B0000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR05)
++ } Else {
++ Return (PR05)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++
++ /* 6:00.0 PCIe x16 */
++ Device (PCE5)
++ {
++ Name (_ADR, 0x000C0000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR06)
++ } Else {
++ Return (PR06)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++
++ /* 7:00.0 PCIe x16 */
++ Device (PCE3)
++ {
++ Name (_ADR, 0x000D0000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR07)
++ } Else {
++ Return (PR07)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++ }
++
++ Device (PWRB) { /* Start Power button device */
++ Name(_HID, EISAID("PNP0C0C"))
++ Name(_UID, 0xAA)
++ Name(_PRW, Package () {3, 0x04}) /* wake from S1-S4 */
++ Name(_STA, 0x0B) /* sata is invisible */
++ }
++ }
++
++#include "acpi/pm_ctrl.asl"
++
++}
+diff --git a/src/mainboard/asus/kgpe-d16/get_bus_conf.c b/src/mainboard/asus/kgpe-d16/get_bus_conf.c
+new file mode 100644
+index 0000000..09fa262
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/get_bus_conf.c
+@@ -0,0 +1,128 @@
++ /*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <console/console.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <string.h>
++#include <stdint.h>
++#include <stdlib.h>
++#if CONFIG_LOGICAL_CPUS
++#include <cpu/amd/multicore.h>
++#endif
++
++#include <cpu/amd/amdfam10_sysconf.h>
++
++/* Global variables for MB layouts and these will be shared by irqtable mptable
++* and acpi_tables busnum is default.
++*/
++u8 bus_isa;
++u8 bus_sr5650[14];
++u8 bus_sp5100[2];
++u32 apicid_sp5100;
++
++/*
++* Here you only need to set value in pci1234 for HT-IO that could be installed or not
++* You may need to preset pci1234 for HTIO board,
++* please refer to src/northbridge/amd/amdk8/get_sblk_pci1234.c for detail
++*/
++u32 pci1234x[] = {
++ 0x0000ff0,
++};
++
++/*
++* HT Chain device num, actually it is unit id base of every ht device in chain,
++* assume every chain only have 4 ht device at most
++*/
++u32 hcdnx[] = {
++ 0x20202020,
++};
++
++
++u32 sbdn_sr5650;
++u32 sbdn_sp5100;
++
++extern void get_pci1234(void);
++
++static u32 get_bus_conf_done = 0;
++
++void get_bus_conf(void)
++{
++ u32 apicid_base;
++ device_t dev;
++ int i;
++
++ if (get_bus_conf_done == 1)
++ return; /* do it only once */
++ get_bus_conf_done = 1;
++
++ sysconf.hc_possible_num = ARRAY_SIZE(pci1234x);
++ for (i = 0; i < sysconf.hc_possible_num; i++) {
++ sysconf.pci1234[i] = pci1234x[i];
++ sysconf.hcdn[i] = hcdnx[i];
++ }
++
++ get_pci1234();
++
++ sysconf.sbdn = (sysconf.hcdn[0] & 0xff);
++ sbdn_sr5650 = sysconf.sbdn;
++ sbdn_sp5100 = 0;
++
++ for (i = 0; i < 2; i++) {
++ bus_sp5100[i] = 0;
++ }
++ for (i = 0; i < ARRAY_SIZE(bus_sr5650); i++) {
++ bus_sr5650[i] = 0;
++ }
++
++
++ bus_sr5650[0] = (sysconf.pci1234[0] >> 16) & 0xff;
++ bus_sp5100[0] = bus_sr5650[0];
++
++
++ /* sp5100 */
++ dev = dev_find_slot(bus_sp5100[0], PCI_DEVFN(sbdn_sp5100 + 0x14, 4));
++ if (dev) {
++ bus_sp5100[1] = pci_read_config8(dev, PCI_SECONDARY_BUS);
++ bus_isa = pci_read_config8(dev, PCI_SUBORDINATE_BUS);
++ bus_isa++;
++ }
++
++ /* sr5650 */
++ for (i = 1; i < ARRAY_SIZE(bus_sr5650); i++) {
++ dev = dev_find_slot(bus_sr5650[0], PCI_DEVFN(sbdn_sr5650 + i, 0));
++ if (dev) {
++ bus_sr5650[i] = pci_read_config8(dev, PCI_SECONDARY_BUS);
++ if(255 != bus_sr5650[i]) {
++ bus_isa = pci_read_config8(dev, PCI_SUBORDINATE_BUS);
++ bus_isa++;
++ }
++ }
++ }
++
++ /* I/O APICs: APIC ID Version State Address */
++ bus_isa = 10;
++#if CONFIG_LOGICAL_CPUS
++ apicid_base = get_apicid_base(1);
++#else
++ apicid_base = CONFIG_MAX_PHYSICAL_CPUS;
++#endif
++ apicid_sp5100 = apicid_base + 0;
++}
+diff --git a/src/mainboard/asus/kgpe-d16/irq_tables.c b/src/mainboard/asus/kgpe-d16/irq_tables.c
+new file mode 100644
+index 0000000..5eb4c24
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/irq_tables.c
+@@ -0,0 +1,112 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <console/console.h>
++#include <device/pci.h>
++#include <string.h>
++#include <stdint.h>
++#include <arch/pirq_routing.h>
++
++#include <cpu/amd/amdfam10_sysconf.h>
++
++static void write_pirq_info(struct irq_info *pirq_info, u8 bus, u8 devfn,
++ u8 link0, u16 bitmap0, u8 link1, u16 bitmap1,
++ u8 link2, u16 bitmap2, u8 link3, u16 bitmap3,
++ u8 slot, u8 rfu)
++{
++ pirq_info->bus = bus;
++ pirq_info->devfn = devfn;
++ pirq_info->irq[0].link = link0;
++ pirq_info->irq[0].bitmap = bitmap0;
++ pirq_info->irq[1].link = link1;
++ pirq_info->irq[1].bitmap = bitmap1;
++ pirq_info->irq[2].link = link2;
++ pirq_info->irq[2].bitmap = bitmap2;
++ pirq_info->irq[3].link = link3;
++ pirq_info->irq[3].bitmap = bitmap3;
++ pirq_info->slot = slot;
++ pirq_info->rfu = rfu;
++}
++extern u8 bus_isa;
++extern u8 bus_rs780[8];
++extern u8 bus_sp5100[2];
++extern unsigned long sbdn_sp5100;
++
++unsigned long write_pirq_routing_table(unsigned long addr)
++{
++ struct irq_routing_table *pirq;
++ struct irq_info *pirq_info;
++ u32 slot_num;
++ u8 *v;
++
++ u8 sum = 0;
++ int i;
++
++ get_bus_conf(); /* it will find out all bus num and apic that share with mptable.c and mptable.c and acpi_tables.c */
++
++ /* Align the table to be 16 byte aligned. */
++ addr += 15;
++ addr &= ~15;
++
++ /* This table must be between 0xf0000 & 0x100000 */
++ printk(BIOS_INFO, "Writing IRQ routing tables to 0x%lx...", addr);
++
++ pirq = (void *)(addr);
++ v = (u8 *) (addr);
++
++ pirq->signature = PIRQ_SIGNATURE;
++ pirq->version = PIRQ_VERSION;
++
++ pirq->rtr_bus = bus_sp5100[0];
++ pirq->rtr_devfn = PCI_DEVFN(0x14, 4);
++
++ pirq->exclusive_irqs = 0;
++
++ pirq->rtr_vendor = 0x1002;
++ pirq->rtr_device = 0x4384;
++
++ pirq->miniport_data = 0;
++
++ memset(pirq->rfu, 0, sizeof(pirq->rfu));
++
++ pirq_info = (void *)(&pirq->checksum + 1);
++ slot_num = 0;
++
++ /* pci bridge */
++ write_pirq_info(pirq_info, bus_sp5100[0], ((sbdn_sp5100 + 0x14) << 3) | 4,
++ 0x1, 0xdef8, 0x2, 0xdef8, 0x3, 0xdef8, 0x4, 0xdef8, 0,
++ 0);
++ pirq_info++;
++ slot_num++;
++
++ pirq->size = 32 + 16 * slot_num;
++
++ for (i = 0; i < pirq->size; i++)
++ sum += v[i];
++
++ sum = pirq->checksum - sum;
++ if (sum != pirq->checksum) {
++ pirq->checksum = sum;
++ }
++
++ printk(BIOS_INFO, "write_pirq_routing_table done.\n");
++
++ return (unsigned long)pirq_info;
++}
+diff --git a/src/mainboard/asus/kgpe-d16/mainboard.c b/src/mainboard/asus/kgpe-d16/mainboard.c
+new file mode 100644
+index 0000000..47ede34
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/mainboard.c
+@@ -0,0 +1,81 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <console/console.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <arch/io.h>
++#include <cpu/x86/msr.h>
++#include <cpu/amd/mtrr.h>
++#include <device/pci_def.h>
++#include <southbridge/amd/sb700/sb700.h>
++#include <southbridge/amd/sr5650/cmn.h>
++
++
++void set_pcie_reset(void);
++void set_pcie_dereset(void);
++
++void set_pcie_reset(void)
++{
++ device_t pcie_core_dev;
++
++ pcie_core_dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ set_htiu_enable_bits(pcie_core_dev, 0xA8, 0xFFFFFFFF, 0x28282828);
++ set_htiu_enable_bits(pcie_core_dev, 0xA9, 0x000000FF, 0x00000028);
++}
++
++void set_pcie_dereset(void)
++{
++ device_t pcie_core_dev;
++
++ pcie_core_dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ set_htiu_enable_bits(pcie_core_dev, 0xA8, 0xFFFFFFFF, 0x6F6F6F6F);
++ set_htiu_enable_bits(pcie_core_dev, 0xA9, 0x000000FF, 0x0000006F);
++}
++
++/*************************************************
++* enable the dedicated function in kgpe-d16 board.
++* This function is called earlier than sr5650_enable.
++*************************************************/
++static void mainboard_enable(device_t dev)
++{
++ printk(BIOS_INFO, "Mainboard KGPE-D16 Enable. dev=0x%p\n", dev);
++
++ msr_t msr, msr2;
++
++ /* TOP_MEM: the top of DRAM below 4G */
++ msr = rdmsr(TOP_MEM);
++ printk
++ (BIOS_INFO, "%s, TOP MEM: msr.lo = 0x%08x, msr.hi = 0x%08x\n",
++ __func__, msr.lo, msr.hi);
++
++ /* TOP_MEM2: the top of DRAM above 4G */
++ msr2 = rdmsr(TOP_MEM2);
++ printk
++ (BIOS_INFO, "%s, TOP MEM2: msr2.lo = 0x%08x, msr2.hi = 0x%08x\n",
++ __func__, msr2.lo, msr2.hi);
++
++ set_pcie_dereset();
++ /* get_ide_dma66(); */
++}
++
++struct chip_operations mainboard_ops = {
++ .enable_dev = mainboard_enable,
++};
+diff --git a/src/mainboard/asus/kgpe-d16/mb_sysconf.h b/src/mainboard/asus/kgpe-d16/mb_sysconf.h
+new file mode 100644
+index 0000000..3a407a6
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/mb_sysconf.h
+@@ -0,0 +1,44 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#ifndef MB_SYSCONF_H
++
++#define MB_SYSCONF_H
++
++struct mb_sysconf_t {
++ u8 bus_isa;
++ u8 bus_8132_0;
++ u8 bus_8132_1;
++ u8 bus_8132_2;
++ u8 bus_8111_0;
++ u8 bus_8111_1;
++ u8 bus_8132a[31][3];
++ u8 bus_8151[31][2];
++
++ u32 apicid_8111;
++ u32 apicid_8132_1;
++ u32 apicid_8132_2;
++ u32 apicid_8132a[31][2];
++ u32 sbdn3;
++ u32 sbdn3a[31];
++ u32 sbdn5[31];
++};
++
++#endif
+diff --git a/src/mainboard/asus/kgpe-d16/mptable.c b/src/mainboard/asus/kgpe-d16/mptable.c
+new file mode 100644
+index 0000000..0279f8b
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/mptable.c
+@@ -0,0 +1,231 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <console/console.h>
++#include <arch/smp/mpspec.h>
++#include <device/pci.h>
++#include <arch/io.h>
++#include <string.h>
++#include <stdint.h>
++#include <cpu/amd/amdfam10_sysconf.h>
++
++extern u8 bus_sr5650[14];
++extern u8 bus_sp5100[2];
++
++extern u32 apicid_sp5100;
++
++extern u32 sbdn_sr5650;
++extern u32 sbdn_sp5100;
++
++
++static void *smp_write_config_table(void *v)
++{
++ struct mp_config_table *mc;
++ int bus_isa;
++ u32 apicid_sr5650;
++ device_t dev;
++ uint8_t sp5100_bus_number;
++
++ mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN);
++
++ mptable_init(mc, LOCAL_APIC_ADDR);
++
++ smp_write_processors(mc);
++
++ get_bus_conf();
++
++ apicid_sp5100 = 0x20;
++ apicid_sr5650 = apicid_sp5100 + 1;
++
++ mptable_write_buses(mc, NULL, &bus_isa);
++ /* I/O APICs: APIC ID Version State Address */
++ {
++ uint32_t *dword_ptr;
++ uint32_t dword;
++ uint16_t word;
++ uint8_t byte;
++
++ sp5100_bus_number = 0; //bus_sp5100[0]; TODO: why bus_sp5100[0] use same value of bus_sr5650[0] assigned by get_pci1234(), instead of 0.
++
++ dev = dev_find_slot(sp5100_bus_number, PCI_DEVFN(sbdn_sp5100 + 0x14, 0));
++ if (dev) {
++ dword_ptr = (u32 *)(pci_read_config32(dev, 0x74) & 0xfffffff0);
++ smp_write_ioapic(mc, apicid_sp5100, 0x11, dword_ptr);
++
++ /* Initialize interrupt mapping */
++ /* USB 1 & 2 */
++ word = pci_read_config16(dev, 0xbe);
++ word &= ~0x3f3f;
++ word |= 0x0; /* 0: INTA, ...., 7: INTH */
++ word |= (0x1 << 3); /* 0: INTA, ...., 7: INTH */
++ word |= (0x2 << 8); /* 0: INTA, ...., 7: INTH */
++ word |= (0x3 << 11); /* 0: INTA, ...., 7: INTH */
++ pci_write_config16(dev, 0xbe, word);
++
++ /* USB 3 */
++ byte = pci_read_config8(dev, 0x63);
++ byte &= 0xf8;
++ byte |= (0x2 << 4); /* 0: INTA, ...., 7: INTH */
++ pci_write_config8(dev, 0x63, byte);
++
++ dword = pci_read_config32(dev, 0xac);
++
++ /* SATA */
++ dword &= ~(7 << 26);
++ dword |= (0x6 << 26); /* 0: INTA, ...., 7: INTH */
++
++ /* Hide IDE */
++ dword &= ~(0x00080000);
++
++ /* dword_ptr |= 1<<22; PIC and APIC co exists */
++ pci_write_config32(dev, 0xac, dword);
++
++ /*
++ * 00:12.0: PROG SATA : INT F
++ * 00:13.0: INTA USB_0
++ * 00:13.1: INTB USB_1
++ * 00:13.2: INTC USB_2
++ * 00:13.3: INTD USB_3
++ * 00:13.4: INTC USB_4
++ * 00:13.5: INTD USB2
++ * 00:14.1: INTA IDE
++ * 00:14.2: Prog HDA : INT E
++ * 00:14.5: INTB ACI
++ * 00:14.6: INTB MCI
++ */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ if (dev) {
++ pci_write_config32(dev, 0xF8, 0x1);
++ dword_ptr = (u32 *)(pci_read_config32(dev, 0xFC) & 0xfffffff0);
++ smp_write_ioapic(mc, apicid_sr5650, 0x11, dword_ptr);
++ }
++ }
++
++ /* I/O Ints: Type Polarity Trigger Bus ID IRQ APIC ID PIN# */
++#define IO_LOCAL_INT(type, intr, apicid, pin) \
++ smp_write_lintsrc(mc, (type), MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH, bus_isa, (intr), (apicid), (pin));
++
++ mptable_add_isa_interrupts(mc, bus_isa, apicid_sp5100, 0);
++
++ /* SR5650 devices */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((2)<<2)|(0)), apicid_sr5650, 28); /* Device 2 (LNKE) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((4)<<2)|(0)), apicid_sr5650, 28); /* Device 4 (LNKF) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((9)<<2)|(0)), apicid_sr5650, 29); /* Device 9 (LNKG) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((10)<<2)|(0)), apicid_sr5650, 30); /* Device 10 (LNKG) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((11)<<2)|(0)), apicid_sr5650, 30); /* Device 11 (LNKG) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((12)<<2)|(0)), apicid_sr5650, 30); /* Device 12 (LNKG) */
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, 0, (((13)<<2)|(0)), apicid_sr5650, 30); /* Device 13 (LNKG) */
++
++ dev = dev_find_slot(0, PCI_DEVFN(0x2, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0x2)|(0)), apicid_sr5650, 0); /* card behind dev2 */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0x4, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0x4)|(0)), apicid_sr5650, 0); /* PIKE */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0x9, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0x9)|(0)), apicid_sr5650, 23); /* NIC A */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0xa, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0xa)|(0)), apicid_sr5650, 24); /* NIC B */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0xb, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0xb)|(0)), apicid_sr5650, 0); /* card behind dev11 */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0xc, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0xc)|(0)), apicid_sr5650, 0); /* card behind dev12 */
++ }
++ dev = dev_find_slot(0, PCI_DEVFN(0xd, 0));
++ if (dev && dev->enabled) {
++ uint8_t bus_pci = dev->link_list->secondary;
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, bus_pci, (((0)<<0xd)|(0)), apicid_sr5650, 0); /* card behind dev13 */
++ }
++
++ /* PCI interrupts are level triggered, and are
++ * associated with a specific bus/device/function tuple.
++ */
++#define PCI_INT(bus, dev, interrupt_signal, pin) \
++ smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, (bus), (((dev)<<2)|(interrupt_signal)), apicid_sp5100, (pin))
++
++ /* USB1 */
++ PCI_INT(sp5100_bus_number, 0x12, 0x0, 0x10); /* OHCI0 Port 0~2 */
++ PCI_INT(sp5100_bus_number, 0x12, 0x1, 0x11); /* OHCI1 Port 3~5 */
++
++ /* USB2 */
++ PCI_INT(sp5100_bus_number, 0x13, 0x0, 0x12); /* OHCI0 Port 6~8 */
++ PCI_INT(sp5100_bus_number, 0x13, 0x1, 0x13); /* EHCI Port 6~11 */
++
++ /* USB3 */
++ PCI_INT(sp5100_bus_number, 0x14, 0x3, 0x12); /* OHCI0 Port 12~13 */
++
++ /* SATA */
++ PCI_INT(sp5100_bus_number, 0x11, 0x0, 0x16); /* 6, INTG */
++
++ /* PCI slots */
++ dev = dev_find_slot(0, PCI_DEVFN(0x14, 4));
++ if (dev && dev->enabled) {
++ u8 bus_pci = dev->link_list->secondary;
++
++ /* PCI_SLOT 0. */
++ PCI_INT(bus_pci, 0x1, 0x0, 0x15);
++ PCI_INT(bus_pci, 0x1, 0x1, 0x16);
++ PCI_INT(bus_pci, 0x1, 0x2, 0x17);
++ PCI_INT(bus_pci, 0x1, 0x3, 0x14);
++
++ /* PCI_SLOT 1. */
++ PCI_INT(bus_pci, 0x2, 0x0, 0x14);
++ PCI_INT(bus_pci, 0x2, 0x1, 0x15);
++ PCI_INT(bus_pci, 0x2, 0x2, 0x16);
++ PCI_INT(bus_pci, 0x2, 0x3, 0x17);
++
++ /* PCI_SLOT 2. */
++ PCI_INT(bus_pci, 0x3, 0x0, 0x16);
++ PCI_INT(bus_pci, 0x3, 0x1, 0x17);
++ PCI_INT(bus_pci, 0x3, 0x2, 0x14);
++ PCI_INT(bus_pci, 0x3, 0x3, 0x15);
++ }
++
++ /*Local Ints: Type Polarity Trigger Bus ID IRQ APIC ID PIN# */
++ IO_LOCAL_INT(mp_ExtINT, 0x0, MP_APIC_ALL, 0x0);
++ IO_LOCAL_INT(mp_NMI, 0x0, MP_APIC_ALL, 0x1);
++ /* There is no extension information... */
++
++ /* Compute the checksums */
++ return mptable_finalize(mc);
++}
++
++unsigned long write_smp_table(unsigned long addr)
++{
++ void *v;
++ v = smp_write_floating_table(addr, 0);
++ return (unsigned long)smp_write_config_table(v);
++}
+diff --git a/src/mainboard/asus/kgpe-d16/resourcemap.c b/src/mainboard/asus/kgpe-d16/resourcemap.c
+new file mode 100644
+index 0000000..3e240dc
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/resourcemap.c
+@@ -0,0 +1,284 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * Copyright (C) 2007 AMD
++ * Written by Yinghai Lu <yinghailu@amd.com> for AMD.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++static void setup_mb_resource_map(void)
++{
++ static const unsigned int register_values[] = {
++ /* Careful set limit registers before base registers which contain the enables */
++ /* DRAM Limit i Registers
++ * F1:0x44 i = 0
++ * F1:0x4C i = 1
++ * F1:0x54 i = 2
++ * F1:0x5C i = 3
++ * F1:0x64 i = 4
++ * F1:0x6C i = 5
++ * F1:0x74 i = 6
++ * F1:0x7C i = 7
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 7: 3] Reserved
++ * [10: 8] Interleave select
++ * specifies the values of A[14:12] to use with interleave enable.
++ * [15:11] Reserved
++ * [31:16] DRAM Limit Address i Bits 39-24
++ * This field defines the upper address bits of a 40 bit address
++ * that define the end of the DRAM region.
++ */
++ // PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x44), 0x0000f8f8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x4C), 0x0000f8f8, 0x00000001,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x54), 0x0000f8f8, 0x00000002,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x5C), 0x0000f8f8, 0x00000003,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x64), 0x0000f8f8, 0x00000004,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x6C), 0x0000f8f8, 0x00000005,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x74), 0x0000f8f8, 0x00000006,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x7C), 0x0000f8f8, 0x00000007,
++
++ /* DRAM Base i Registers
++ * F1:0x40 i = 0
++ * F1:0x48 i = 1
++ * F1:0x50 i = 2
++ * F1:0x58 i = 3
++ * F1:0x60 i = 4
++ * F1:0x68 i = 5
++ * F1:0x70 i = 6
++ * F1:0x78 i = 7
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 7: 2] Reserved
++ * [10: 8] Interleave Enable
++ * 000 = No interleave
++ * 001 = Interleave on A[12] (2 nodes)
++ * 010 = reserved
++ * 011 = Interleave on A[12] and A[14] (4 nodes)
++ * 100 = reserved
++ * 101 = reserved
++ * 110 = reserved
++ * 111 = Interleve on A[12] and A[13] and A[14] (8 nodes)
++ * [15:11] Reserved
++ * [31:16] DRAM Base Address i Bits 39-24
++ * This field defines the upper address bits of a 40-bit address
++ * that define the start of the DRAM region.
++ */
++ // PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x40), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x48), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x50), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x58), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x60), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x68), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x70), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x78), 0x0000f8fc, 0x00000000,
++
++ /* Memory-Mapped I/O Limit i Registers
++ * F1:0x84 i = 0
++ * F1:0x8C i = 1
++ * F1:0x94 i = 2
++ * F1:0x9C i = 3
++ * F1:0xA4 i = 4
++ * F1:0xAC i = 5
++ * F1:0xB4 i = 6
++ * F1:0xBC i = 7
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 3: 3] Reserved
++ * [ 5: 4] Destination Link ID
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 = Link 3
++ * [ 6: 6] Reserved
++ * [ 7: 7] Non-Posted
++ * 0 = CPU writes may be posted
++ * 1 = CPU writes must be non-posted
++ * [31: 8] Memory-Mapped I/O Limit Address i (39-16)
++ * This field defines the upp adddress bits of a 40-bit address that
++ * defines the end of a memory-mapped I/O region n
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x84), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x8C), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x94), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x9C), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA4), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xAC), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB4), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xBC), 0x00000048, 0x00000000,
++
++ /* Memory-Mapped I/O Base i Registers
++ * F1:0x80 i = 0
++ * F1:0x88 i = 1
++ * F1:0x90 i = 2
++ * F1:0x98 i = 3
++ * F1:0xA0 i = 4
++ * F1:0xA8 i = 5
++ * F1:0xB0 i = 6
++ * F1:0xB8 i = 7
++ * [ 0: 0] Read Enable
++ * 0 = Reads disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes disabled
++ * 1 = Writes Enabled
++ * [ 2: 2] Cpu Disable
++ * 0 = Cpu can use this I/O range
++ * 1 = Cpu requests do not use this I/O range
++ * [ 3: 3] Lock
++ * 0 = base/limit registers i are read/write
++ * 1 = base/limit registers i are read-only
++ * [ 7: 4] Reserved
++ * [31: 8] Memory-Mapped I/O Base Address i (39-16)
++ * This field defines the upper address bits of a 40bit address
++ * that defines the start of memory-mapped I/O region i
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x80), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x88), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x90), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x98), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA0), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA8), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB0), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB8), 0x000000f0, 0x00000000,
++
++ /* PCI I/O Limit i Registers
++ * F1:0xC4 i = 0
++ * F1:0xCC i = 1
++ * F1:0xD4 i = 2
++ * F1:0xDC i = 3
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 3: 3] Reserved
++ * [ 5: 4] Destination Link ID
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 = Link 3
++ * [11: 6] Reserved
++ * [24:12] PCI I/O Limit Address i
++ * This field defines the end of PCI I/O region n
++ * [31:25] Reserved
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC4), 0xFE000FC8, 0x00fff110, /* link 3 of cpu 0 --> AMD SR5690 */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xCC), 0xFE000FC8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD4), 0xFE000FC8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xDC), 0xFE000FC8, 0x00000000,
++
++ /* PCI I/O Base i Registers
++ * F1:0xC0 i = 0
++ * F1:0xC8 i = 1
++ * F1:0xD0 i = 2
++ * F1:0xD8 i = 3
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 3: 2] Reserved
++ * [ 4: 4] VGA Enable
++ * 0 = VGA matches Disabled
++ * 1 = matches all address < 64K and where A[9:0] is in the
++ * range 3B0-3BB or 3C0-3DF independent of the base & limit registers
++ * [ 5: 5] ISA Enable
++ * 0 = ISA matches Disabled
++ * 1 = Blocks address < 64K and in the last 768 bytes of eack 1K block
++ * from matching agains this base/limit pair
++ * [11: 6] Reserved
++ * [24:12] PCI I/O Base i
++ * This field defines the start of PCI I/O region n
++ * [31:25] Reserved
++ */
++// PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC0), 0xFE000FCC, 0x00001013,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC8), 0xFE000FCC, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD0), 0xFE000FCC, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD8), 0xFE000FCC, 0x00000000,
++
++ /* Config Base and Limit i Registers
++ * F1:0xE0 i = 0
++ * F1:0xE4 i = 1
++ * F1:0xE8 i = 2
++ * F1:0xEC i = 3
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 2: 2] Device Number Compare Enable
++ * 0 = The ranges are based on bus number
++ * 1 = The ranges are ranges of devices on bus 0
++ * [ 3: 3] Reserved
++ * [ 6: 4] Destination Node
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 7: 7] Reserved
++ * [ 9: 8] Destination Link
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 - Link 3
++ * [15:10] Reserved
++ * [23:16] Bus Number Base i
++ * This field defines the lowest bus number in configuration region i
++ * [31:24] Bus Number Limit i
++ * This field defines the highest bus number in configuration region i
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE0), 0x0000FC88, 0x05000303, /* link 3 of cpu 0 --> AMD SR5690 */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE4), 0x0000FC88, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE8), 0x0000FC88, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xEC), 0x0000FC88, 0x00000000,
++
++ };
++
++ int max;
++ max = ARRAY_SIZE(register_values);
++ setup_resource_map(register_values, max);
++}
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+new file mode 100644
+index 0000000..9964cfe
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -0,0 +1,422 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * Copyright (C) 2007 AMD
++ * Written by Yinghai Lu <yinghailu@amd.com> for AMD.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <stdint.h>
++#include <string.h>
++#include <reset.h>
++#include <device/pci_def.h>
++#include <device/pci_ids.h>
++#include <arch/io.h>
++#include <device/pnp_def.h>
++#include <cpu/x86/lapic.h>
++#include <console/console.h>
++#include <timestamp.h>
++#include <lib.h>
++#include <spd.h>
++#include <cpu/amd/model_10xxx_rev.h>
++#include <northbridge/amd/amdfam10/raminit.h>
++#include <northbridge/amd/amdfam10/amdfam10.h>
++#include "lib/delay.c"
++#include <cpu/x86/lapic.h>
++#include "northbridge/amd/amdfam10/reset_test.c"
++#include <superio/nuvoton/common/nuvoton.h>
++#include <superio/nuvoton/nct5572d/nct5572d.h>
++#include <cpu/x86/bist.h>
++// #include "northbridge/amd/amdk8/incoherent_ht.c"
++#include <southbridge/amd/sb700/sb700.h>
++#include <southbridge/amd/sb700/smbus.h>
++#include <southbridge/amd/sr5650/sr5650.h>
++#include "northbridge/amd/amdfam10/debug.c"
++#include "northbridge/amd/amdfam10/setup_resource_map.c"
++
++#define SERIAL_DEV PNP_DEV(0x2e, NCT5572D_SP1)
++
++static void activate_spd_rom(const struct mem_controller *ctrl);
++
++static inline int spd_read_byte(unsigned device, unsigned address)
++{
++ return do_smbus_read_byte(SMBUS_AUX_IO_BASE, device, address);
++}
++
++#include <northbridge/amd/amdfam10/amdfam10.h>
++#include "northbridge/amd/amdfam10/raminit_sysinfo_in_ram.c"
++#include "northbridge/amd/amdfam10/pci.c"
++#include "resourcemap.c"
++#include "cpu/amd/quadcore/quadcore.c"
++
++#include <cpu/amd/microcode.h>
++
++#include "cpu/amd/model_10xxx/init_cpus.c"
++#include "northbridge/amd/amdfam10/early_ht.c"
++
++/*
++ * ASUS KGPE-D16 specific SPD enable/disable magic.
++ *
++ * Setting SP5100 GPIOs 59 and 60 controls an SPI mux with four settings:
++ * 0: Disabled
++ * 1: Normal SPI access
++ * 2: CPU0 SPD
++ * 3: CPU1 SPD
++ *
++ * Disable SPD access after RAM init to allow access to standard SMBus/I2C offsets
++ * which is required e.g. by lm-sensors.
++ */
++
++/* Relevant GPIO register information is available in the
++ * AMD SP5100 Register Reference Guide rev. 3.03, page 130
++ */
++static void switch_spd_mux(uint8_t channel)
++{
++ uint8_t byte;
++
++ byte = pci_read_config8(PCI_DEV(0, 0x14, 0), 0x54);
++ byte &= ~0xc; /* Clear SPD mux GPIOs */
++ byte &= ~0xc0; /* Enable SPD mux GPIO output drivers */
++ byte |= (channel << 2) & 0xc; /* Set SPD mux GPIOs */
++ pci_write_config8(PCI_DEV(0, 0x14, 0), 0x54, byte);
++}
++
++static const uint8_t spd_addr[] = {
++ // Socket 0 Node 0 ("Node 0")
++ RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++ // Socket 0 Node 1 ("Node 1")
++ RC00, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++ // Socket 1 Node 1 ("Node 2")
++ RC01, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++ // Socket 1 Node 0 ("Node 3")
++ RC01, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++};
++
++static void activate_spd_rom(const struct mem_controller *ctrl) {
++ struct sys_info *sysinfo = &sysinfo_car;
++
++ printk(BIOS_DEBUG, "activate_spd_rom() for node %02x\n", ctrl->node_id);
++ if (ctrl->node_id == 0) {
++ printk(BIOS_DEBUG, "enable_spd_node0()\n");
++ switch_spd_mux(0x2);
++ } else if (ctrl->node_id == 1) {
++ printk(BIOS_DEBUG, "enable_spd_node1()\n");
++ switch_spd_mux((sysinfo->nodes <= 2)?0x2:0x3);
++ } else if (ctrl->node_id == 2) {
++ printk(BIOS_DEBUG, "enable_spd_node2()\n");
++ switch_spd_mux((sysinfo->nodes <= 2)?0x3:0x2);
++ } else if (ctrl->node_id == 3) {
++ printk(BIOS_DEBUG, "enable_spd_node3()\n");
++ switch_spd_mux(0x3);
++ }
++}
++
++/* Voltages are specified by index
++ * Valid indicies for this platform are:
++ * 0: 1.5V
++ * 1: 1.35V
++ * 2: 1.25V
++ * 3: 1.15V
++ */
++static void set_ddr3_voltage(uint8_t node, uint8_t index) {
++ uint8_t byte;
++ uint8_t value;
++
++ if (index == 0)
++ value = 0x0;
++ else if (index == 1)
++ value = 0x1;
++ else if (index == 2)
++ value = 0x4;
++ else if (index == 3)
++ value = 0x5;
++ if (node == 1)
++ value <<= 1;
++
++ /* Set GPIOs */
++ byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0xd1);
++ if (node == 0)
++ byte &= ~0x5;
++ if (node == 1)
++ byte &= ~0xa;
++ byte |= value;
++ pci_write_config8(PCI_DEV(0, 0x14, 3), 0xd1, byte);
++
++ /* Enable GPIO output drivers */
++ byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0xd0);
++ byte &= 0x0f;
++ pci_write_config8(PCI_DEV(0, 0x14, 3), 0xd0, byte);
++}
++
++static void set_peripheral_control_lines(void) {
++ uint8_t byte;
++
++ /* Enable PCICLK5 (onboard FireWire device) */
++ outb(0x41, 0xcd6);
++ outb(0x02, 0xcd7);
++
++ /* Enable the RTC AltCentury register */
++ outb(0x41, 0xcd6);
++ byte = inb(0xcd7);
++ byte |= 0x10;
++ outb(byte, 0xcd7);
++}
++
++#ifdef TEST_MEMORY
++static void execute_memory_test(void)
++{
++ /* Test DRAM functionality */
++ uint32_t i;
++ uint32_t* dataptr;
++ printk(BIOS_DEBUG, "Writing test patterns to memory...\n");
++ for (i=0; i < 0x1000000; i = i + 8) {
++ dataptr = (void *)(0x300000 + i);
++ *dataptr = 0x55555555;
++ dataptr = (void *)(0x300000 + i + 4);
++ *dataptr = 0xaaaaaaaa;
++ }
++ printk(BIOS_DEBUG, "Done!\n");
++ printk(BIOS_DEBUG, "Testing memory...\n");
++ uint32_t readback;
++ for (i=0; i < 0x1000000; i = i + 8) {
++ dataptr = (void *)(0x300000 + i);
++ readback = *dataptr;
++ if (readback != 0x55555555)
++ printk(BIOS_DEBUG, "%p: INCORRECT VALUE %08x (should have been %08x)\n", dataptr, readback, 0x55555555);
++ dataptr = (void *)(0x300000 + i + 4);
++ readback = *dataptr;
++ if (readback != 0xaaaaaaaa)
++ printk(BIOS_DEBUG, "%p: INCORRECT VALUE %08x (should have been %08x)\n", dataptr, readback, 0xaaaaaaaa);
++ }
++ printk(BIOS_DEBUG, "Done!\n");
++}
++#endif
++
++void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
++{
++ struct sys_info *sysinfo = &sysinfo_car;
++
++ u32 bsp_apicid = 0, val;
++ msr_t msr;
++
++ timestamp_init(timestamp_get());
++ timestamp_add_now(TS_START_ROMSTAGE);
++
++ if (!cpu_init_detectedx && boot_cpu()) {
++ /* Nothing special needs to be done to find bus 0 */
++ /* Allow the HT devices to be found */
++ set_bsp_node_CHtExtNodeCfgEn();
++ enumerate_ht_chain();
++
++ /* SR56x0 pcie bridges block pci_locate_device() before pcie training.
++ * disable all pcie bridges on SR56x0 to work around it
++ */
++ sr5650_disable_pcie_bridge();
++
++ /* Initialize southbridge */
++ sb7xx_51xx_pci_port80();
++
++ /* Initialize early serial */
++ nuvoton_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
++ console_init();
++ }
++
++ post_code(0x30);
++
++ if (bist == 0)
++ bsp_apicid = init_cpus(cpu_init_detectedx, sysinfo);
++
++ post_code(0x32);
++
++ enable_sr5650_dev8();
++ sb7xx_51xx_lpc_init();
++
++ if (CONFIG_MAX_PHYSICAL_CPUS != 4)
++ printk(BIOS_WARNING, "CONFIG_MAX_PHYSICAL_CPUS is %d, but this is a dual socket AMD G34 board!\n", CONFIG_MAX_PHYSICAL_CPUS);
++
++ /* Halt if there was a built in self test failure */
++ report_bist_failure(bist);
++
++ val = cpuid_eax(1);
++ printk(BIOS_DEBUG, "BSP Family_Model: %08x\n", val);
++ printk(BIOS_DEBUG, "*sysinfo range: [%p,%p]\n",sysinfo,sysinfo+1);
++ printk(BIOS_DEBUG, "bsp_apicid = %02x\n", bsp_apicid);
++ printk(BIOS_DEBUG, "cpu_init_detectedx = %08lx\n", cpu_init_detectedx);
++
++ /* Setup sysinfo defaults */
++ set_sysinfo_in_ram(0);
++
++ update_microcode(val);
++
++ post_code(0x33);
++
++ cpuSetAMDMSR();
++ post_code(0x34);
++
++ amd_ht_init(sysinfo);
++ amd_ht_fixup(sysinfo);
++ post_code(0x35);
++
++ /* Set DDR memory voltage
++ * FIXME
++ * This should be set based on the output of the DIMM SPDs
++ * For now it is locked to 1.5V
++ */
++ set_ddr3_voltage(0, 0); /* Node 0 */
++ set_ddr3_voltage(1, 0); /* Node 1 */
++
++ /* Setup nodes PCI space and start core 0 AP init. */
++ finalize_node_setup(sysinfo);
++
++ /* Setup any mainboard PCI settings etc. */
++ setup_mb_resource_map();
++ post_code(0x36);
++
++ /* wait for all the APs core0 started by finalize_node_setup. */
++ /* FIXME: A bunch of cores are going to start output to serial at once.
++ * It would be nice to fix up prink spinlocks for ROM XIP mode.
++ * I think it could be done by putting the spinlock flag in the cache
++ * of the BSP located right after sysinfo.
++ */
++ wait_all_core0_started();
++
++ /* run _early_setup before soft-reset. */
++ sr5650_early_setup();
++ sb7xx_51xx_early_setup();
++
++ if (IS_ENABLED(CONFIG_SET_FIDVID)) {
++ msr = rdmsr(0xc0010071);
++ printk(BIOS_DEBUG, "\nBegin FIDVID MSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
++
++ /* FIXME: The sb fid change may survive the warm reset and only need to be done once */
++ enable_fid_change_on_sb(sysinfo->sbbusn, sysinfo->sbdn);
++
++ post_code(0x39);
++
++ if (!warm_reset_detect(0)) { // BSP is node 0
++ init_fidvid_bsp(bsp_apicid, sysinfo->nodes);
++ } else {
++ init_fidvid_stage2(bsp_apicid, 0); // BSP is node 0
++ }
++
++ post_code(0x3A);
++
++ /* show final fid and vid */
++ msr=rdmsr(0xc0010071);
++ printk(BIOS_DEBUG, "End FIDVIDMSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
++ }
++
++ if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
++ /* Core0 on each node is configured. Now setup any additional cores. */
++ printk(BIOS_DEBUG, "start_other_cores()\n");
++ start_other_cores();
++ post_code(0x37);
++ wait_all_other_cores_started(bsp_apicid);
++ }
++
++ post_code(0x38);
++
++ init_timer(); // Need to use TMICT to synconize FID/VID
++
++ sr5650_htinit();
++
++ /* Reset for HT, FIDVID, PLL and errata changes to take affect. */
++ if (!warm_reset_detect(0)) {
++ printk(BIOS_INFO, "...WARM RESET...\n\n\n");
++ soft_reset();
++ die("After soft_reset_x - shouldn't see this message!!!\n");
++ }
++
++ /* Set up peripheral control lines */
++ set_peripheral_control_lines();
++
++ post_code(0x3B);
++
++ /* It's the time to set ctrl in sysinfo now; */
++ printk(BIOS_DEBUG, "fill_mem_ctrl() detected %d nodes\n", sysinfo->nodes);
++ fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr);
++ post_code(0x3D);
++
++#if 0
++ /* FIXME
++ * After the AMD K10 code has been converted to use
++ * IS_ENABLED(CONFIG_DEBUG_SMBUS) uncomment this block
++ */
++ if (IS_ENABLED(CONFIG_DEBUG_SMBUS)) {
++ dump_spd_registers(&cpu[0]);
++ dump_smbus_registers();
++ }
++#endif
++
++ post_code(0x40);
++
++ timestamp_add_now(TS_BEFORE_INITRAM);
++ printk(BIOS_DEBUG, "raminit_amdmct()\n");
++ raminit_amdmct(sysinfo);
++ timestamp_add_now(TS_AFTER_INITRAM);
++
++#if !IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
++ cbmem_initialize_empty();
++ post_code(0x41);
++
++ amdmct_cbmem_store_info(sysinfo);
++#endif
++
++ printk(BIOS_DEBUG, "disable_spd()\n");
++ switch_spd_mux(0x1);
++
++ sr5650_before_pci_init();
++ sb7xx_51xx_before_pci_init();
++
++ /* Configure SP5100 GPIOs to match vendor settings */
++ pci_write_config16(PCI_DEV(0, 0x14, 0), 0x50, 0x0170);
++ pci_write_config16(PCI_DEV(0, 0x14, 0), 0x54, 0x0707);
++ pci_write_config16(PCI_DEV(0, 0x14, 0), 0x56, 0x0bb0);
++ pci_write_config16(PCI_DEV(0, 0x14, 0), 0x5a, 0x0ff0);
++
++ timestamp_add_now(TS_END_ROMSTAGE);
++
++#ifdef TEST_MEMORY
++ execute_memory_test();
++#endif
++
++ post_cache_as_ram(); // BSP switch stack to ram, copy then execute LB.
++ post_code(0x43); // Should never see this post code.
++}
++
++/**
++ * BOOL AMD_CB_ManualBUIDSwapList(u8 Node, u8 Link, u8 **List)
++ * Description:
++ * This routine is called every time a non-coherent chain is processed.
++ * BUID assignment may be controlled explicitly on a non-coherent chain. Provide a
++ * swap list. The first part of the list controls the BUID assignment and the
++ * second part of the list provides the device to device linking. Device orientation
++ * can be detected automatically, or explicitly. See documentation for more details.
++ *
++ * Automatic non-coherent init assigns BUIDs starting at 1 and incrementing sequentially
++ * based on each device's unit count.
++ *
++ * Parameters:
++ * @param[in] node = The node on which this chain is located
++ * @param[in] link = The link on the host for this chain
++ * @param[out] List = supply a pointer to a list
++ */
++BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
++{
++ return 0;
++}
+\ No newline at end of file
+diff --git a/src/mainboard/asus/kgpe-d16/spd_notes.txt b/src/mainboard/asus/kgpe-d16/spd_notes.txt
+new file mode 100644
+index 0000000..623a88f
+--- /dev/null
++++ b/src/mainboard/asus/kgpe-d16/spd_notes.txt
+@@ -0,0 +1,30 @@
++====================================================================================================
++SPD mux
++====================================================================================================
++ SP5100
++ GPIO 60 GPIO 59
++Disabled 0 0
++Normal operation 0 1
++CPU 0 SPD 1 0
++CPU 1 SPD 1 1
++
++====================================================================================================
++W83795
++====================================================================================================
++
++Sensor mappings:
++CPU_FAN1: FAN1
++CPU_FAN2: FAN2
++FRNT_FAN1: FAN3
++FRNT_FAN2: FAN4
++FRNT_FAN3: FAN5
++FRNT_FAN4: FAN6
++FRNT_FAN5: FAN7
++REAR_FAN1: FAN8
++
++====================================================================================================
++Other hardware
++====================================================================================================
++
++RECOVERY1 middle pin is connected to southbridge (AMD SP5100) GPIO 61
++Normal is HIGH, recovery is LOW.
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0017-mainboard-asus-kgpe-d16-Add-nvram-option-to-enable-d.patch b/resources/libreboot/patch/kgpe-d16/0017-mainboard-asus-kgpe-d16-Add-nvram-option-to-enable-d.patch
new file mode 100644
index 00000000..bfed1586
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0017-mainboard-asus-kgpe-d16-Add-nvram-option-to-enable-d.patch
@@ -0,0 +1,70 @@
+From 09718eb82377722bec9b445cb8f7104e908724c1 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 18:38:06 -0500
+Subject: [PATCH 017/146] mainboard/asus/kgpe-d16: Add nvram option to
+ enable/disable the IEEE1394 controller
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 1 +
+ src/mainboard/asus/kgpe-d16/romstage.c | 21 +++++++++++++++++----
+ 3 files changed, 19 insertions(+), 4 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index cffdd03..e920297 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -12,5 +12,6 @@ ecc_scrub_rate = 1.28us
+ interleave_chip_selects = Enable
+ interleave_nodes = Disable
+ interleave_memory_channels = Enable
++ieee1394 = Enable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index bcf9cd3..b9266dc 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -41,6 +41,7 @@ entries
+ 456 1 e 1 ECC_memory
+ 457 1 e 1 ECC_redirection
+ 458 4 e 11 hypertransport_speed_limit
++477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+ # Reserve the extended AMD configuration registers
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 9964cfe..616fdfb 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -165,10 +165,23 @@ static void set_ddr3_voltage(uint8_t node, uint8_t index) {
+
+ static void set_peripheral_control_lines(void) {
+ uint8_t byte;
+-
+- /* Enable PCICLK5 (onboard FireWire device) */
+- outb(0x41, 0xcd6);
+- outb(0x02, 0xcd7);
++ uint8_t nvram;
++ uint8_t enable_ieee1394;
++
++ enable_ieee1394 = 1;
++
++ if (get_option(&nvram, "ieee1394") == CB_SUCCESS)
++ enable_ieee1394 = nvram & 0x1;
++
++ if (enable_ieee1394) {
++ /* Enable PCICLK5 (onboard FireWire device) */
++ outb(0x41, 0xcd6);
++ outb(0x02, 0xcd7);
++ } else {
++ /* Disable PCICLK5 (onboard FireWire device) */
++ outb(0x41, 0xcd6);
++ outb(0x00, 0xcd7);
++ }
+
+ /* Enable the RTC AltCentury register */
+ outb(0x41, 0xcd6);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0018-cpu-amd-model_10xxx-Clean-up-debugging-statements.patch b/resources/libreboot/patch/kgpe-d16/0018-cpu-amd-model_10xxx-Clean-up-debugging-statements.patch
new file mode 100644
index 00000000..79b7c673
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0018-cpu-amd-model_10xxx-Clean-up-debugging-statements.patch
@@ -0,0 +1,114 @@
+From b4e60bb51855c3edb3c41fbe7f6378a638df9a95 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:46:54 -0500
+Subject: [PATCH 018/146] cpu/amd/model_10xxx: Clean up debugging statements
+
+---
+ src/cpu/amd/model_10xxx/fidvid.c | 43 +++++++++++++++++++-------------------
+ 1 file changed, 21 insertions(+), 22 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 36bdf36..99ffcc8 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -460,35 +461,35 @@ static void config_clk_power_ctrl_reg0(int node, u32 cpuRev, u8 procPkg) {
+
+ static void config_power_ctrl_misc_reg(device_t dev,u32 cpuRev, u8 procPkg) {
+ /* check PVI/SVI */
+- u32 dword = pci_read_config32(dev, 0xA0);
++ u32 dword = pci_read_config32(dev, 0xa0);
+
+- /* BKDG r31116 2010-04-22 2.4.1.7 step b F3xA0[VSSlamVidMod] */
+- /* PllLockTime and PsiVidEn set in ruleset in defaults.h */
++ /* BKDG r31116 2010-04-22 2.4.1.7 step b F3xA0[VSSlamVidMod] */
++ /* PllLockTime and PsiVidEn set in ruleset in defaults.h */
+ if (dword & PVI_MODE) { /* PVI */
+ /* set slamVidMode to 0 for PVI */
+ dword &= VID_SLAM_OFF ;
+ } else { /* SVI */
+ /* set slamVidMode to 1 for SVI */
+ dword |= VID_SLAM_ON;
+- }
++ }
+ /* set the rest of A0 since we're at it... */
+
+- if (cpuRev & (AMD_DA_Cx | AMD_RB_C3 )) {
+- dword |= NB_PSTATE_FORCE_ON;
++ if (cpuRev & (AMD_DA_Cx | AMD_RB_C3 )) {
++ dword |= NB_PSTATE_FORCE_ON;
+ } // else should we clear it ?
+
+
+ if ((procPkg == AMD_PKGTYPE_G34) || (procPkg == AMD_PKGTYPE_C32) ) {
+- dword |= BP_INS_TRI_EN_ON ;
++ dword |= BP_INS_TRI_EN_ON ;
+ }
+
+ /* TODO: look into C1E state and F3xA0[IdleExitEn]*/
+ #if CONFIG_SVI_HIGH_FREQ
+- if (cpuRev & AMD_FAM10_C3) {
+- dword |= SVI_HIGH_FREQ_ON;
+- }
+- #endif
+- pci_write_config32(dev, 0xA0, dword);
++ if (cpuRev & AMD_FAM10_C3) {
++ dword |= SVI_HIGH_FREQ_ON;
++ }
++ #endif
++ pci_write_config32(dev, 0xa0, dword);
+ }
+
+ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
+@@ -581,7 +582,7 @@ static void prep_fid_change(void)
+ nodes = get_nodes();
+
+ for (i = 0; i < nodes; i++) {
+- printk(BIOS_DEBUG, "Prep FID/VID Node:%02x \n", i);
++ printk(BIOS_DEBUG, "Prep FID/VID Node:%02x\n", i);
+ dev = NODE_PCI(i, 3);
+ u32 cpuRev = mctGetLogicalCPUID(0xFF) ;
+ u8 procPkg = mctGetProcessorPackageType();
+@@ -591,25 +592,23 @@ static void prep_fid_change(void)
+ /* Figure out the value for VsSlamTime and program it */
+ recalculateVsSlamTimeSettingOnCorePre(dev);
+
+- config_clk_power_ctrl_reg0(i,cpuRev,procPkg);
++ config_clk_power_ctrl_reg0(i,cpuRev,procPkg);
+
+ config_power_ctrl_misc_reg(dev,cpuRev,procPkg);
+ config_nb_syn_ptr_adj(dev,cpuRev);
+
+- config_acpi_pwr_state_ctrl_regs(dev,cpuRev,procPkg);
++ config_acpi_pwr_state_ctrl_regs(dev,cpuRev,procPkg);
+
+ dword = pci_read_config32(dev, 0x80);
+- printk(BIOS_DEBUG, " F3x80: %08x \n", dword);
++ printk(BIOS_DEBUG, " F3x80: %08x\n", dword);
+ dword = pci_read_config32(dev, 0x84);
+- printk(BIOS_DEBUG, " F3x84: %08x \n", dword);
++ printk(BIOS_DEBUG, " F3x84: %08x\n", dword);
+ dword = pci_read_config32(dev, 0xD4);
+- printk(BIOS_DEBUG, " F3xD4: %08x \n", dword);
++ printk(BIOS_DEBUG, " F3xD4: %08x\n", dword);
+ dword = pci_read_config32(dev, 0xD8);
+- printk(BIOS_DEBUG, " F3xD8: %08x \n", dword);
++ printk(BIOS_DEBUG, " F3xD8: %08x\n", dword);
+ dword = pci_read_config32(dev, 0xDC);
+- printk(BIOS_DEBUG, " F3xDC: %08x \n", dword);
+-
+-
++ printk(BIOS_DEBUG, " F3xDC: %08x\n", dword);
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0019-southbridge-amd-sb700-Add-Suspend-to-RAM-S3-support.patch b/resources/libreboot/patch/kgpe-d16/0019-southbridge-amd-sb700-Add-Suspend-to-RAM-S3-support.patch
new file mode 100644
index 00000000..a097102e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0019-southbridge-amd-sb700-Add-Suspend-to-RAM-S3-support.patch
@@ -0,0 +1,365 @@
+From a0ab6743661d88d7e7c699bc8e506e9b1243749c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:14:25 -0500
+Subject: [PATCH 019/146] southbridge/amd/sb700: Add Suspend to RAM (S3)
+ support
+
+---
+ src/southbridge/amd/sb700/Makefile.inc | 1 +
+ src/southbridge/amd/sb700/bootblock.c | 4 +-
+ src/southbridge/amd/sb700/early_setup.c | 39 ++++++--
+ src/southbridge/amd/sb700/lpc.c | 12 ++-
+ src/southbridge/amd/sb700/sb700.h | 3 +
+ src/southbridge/amd/sb700/spi.c | 148 +++++++++++++++++++++++++++++++
+ src/southbridge/amd/sb700/spi.h | 21 +++++
+ 7 files changed, 216 insertions(+), 12 deletions(-)
+ create mode 100644 src/southbridge/amd/sb700/spi.c
+ create mode 100644 src/southbridge/amd/sb700/spi.h
+
+diff --git a/src/southbridge/amd/sb700/Makefile.inc b/src/southbridge/amd/sb700/Makefile.inc
+index 5ec8431..538a7c1 100644
+--- a/src/southbridge/amd/sb700/Makefile.inc
++++ b/src/southbridge/amd/sb700/Makefile.inc
+@@ -12,6 +12,7 @@ ramstage-y += pci.c
+ ramstage-$(CONFIG_HAVE_ACPI_TABLES) += fadt.c
+ romstage-y += reset.c
+ ramstage-y += reset.c
++ramstage-y += spi.c
+ romstage-$(CONFIG_USBDEBUG_IN_ROMSTAGE) += enable_usbdebug.c
+ ramstage-$(CONFIG_USBDEBUG) += enable_usbdebug.c
+
+diff --git a/src/southbridge/amd/sb700/bootblock.c b/src/southbridge/amd/sb700/bootblock.c
+index 8f722a8..46e5597 100644
+--- a/src/southbridge/amd/sb700/bootblock.c
++++ b/src/southbridge/amd/sb700/bootblock.c
+@@ -71,7 +71,7 @@ static void sb700_enable_rom(void)
+ /* Enable LPC ROM range end at 0xffff(ffff). */
+ pci_io_write_config16(dev, 0x6e, 0xffff);
+
+- /* SB700 LPC Bridge 0x48h.
++ /* SB700 LPC Bridge 0x48.
+ * Turn on all LPC IO Port decode enables
+ */
+ dword = pci_io_read_config32(dev, 0x44);
+@@ -89,7 +89,7 @@ static void sb700_enable_rom(void)
+ reg8 |= (1<<0) | (1<<1) | (1<<4) | (1<<6);
+ pci_io_write_config8(dev, 0x48, reg8);
+
+- /* SB700 LPC Bridge 0x4ah.
++ /* SB700 LPC Bridge 0x4a.
+ * BIT4: Port Enable for Port 0x80
+ */
+ reg8 = pci_io_read_config8(dev, 0x4a);
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index de3fa97..a6849b0 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -474,8 +474,10 @@ static void sb700_devices_por_init(void)
+ /* LPC Device, BDF:0-20-3 */
+ printk(BIOS_INFO, "sb700_devices_por_init(): LPC Device, BDF:0-20-3\n");
+ dev = pci_locate_device(PCI_ID(0x1002, 0x439D), 0);
+- /* DMA enable */
+- pci_write_config8(dev, 0x40, 0x04);
++ if (!IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA)) {
++ /* DMA enable */
++ pci_write_config8(dev, 0x40, 0x04);
++ }
+
+ /* IO Port Decode Enable */
+ pci_write_config8(dev, 0x44, 0xFF);
+@@ -618,6 +620,17 @@ static void sb700_pmio_por_init(void)
+ byte = pmio_read(0xB2);
+ byte |= 1 << 0;
+ pmio_write(0xB2, byte);
++
++ // FIXME: Enabling this causes boot to hang while initializing processors.
++// /* Enable automatic C1e state switch */
++// byte = pmio_read(0xc9);
++// byte |= 0x11;
++// pmio_write(0xc9, byte);
++
++ /* Enable precision HPET clock and automatic C state switch */
++ byte = pmio_read(0xbb);
++ byte |= 0xc0;
++ pmio_write(0xbb, byte);
+ }
+
+ /*
+@@ -653,10 +666,12 @@ static void sb700_pci_cfg(void)
+ * mentioned in RPR. But I keep them. The registers and the
+ * comments are compatible. */
+ dev = pci_locate_device(PCI_ID(0x1002, 0x439D), 0);
+- /* Enabling LPC DMA function. */
+- byte = pci_read_config8(dev, 0x40);
+- byte |= (1 << 2);
+- pci_write_config8(dev, 0x40, byte);
++ if (!IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA)) {
++ /* Enabling LPC DMA function. */
++ byte = pci_read_config8(dev, 0x40);
++ byte |= (1 << 2);
++ pci_write_config8(dev, 0x40, byte);
++ }
+ /* Disabling LPC TimeOut. 0x48[7] clear. */
+ byte = pci_read_config8(dev, 0x48);
+ byte &= 0x7f;
+@@ -746,6 +761,18 @@ int acpi_get_sleep_type(void)
+ return ((tmp & (7 << 10)) >> 10);
+ }
+
++void set_lpc_sticky_ctl(bool enable)
++{
++ uint8_t byte;
++
++ byte = pmio_read(0xbb);
++ if (enable)
++ byte |= 0x20;
++ else
++ byte &= ~0x20;
++ pmio_write(0xbb, byte);
++}
++
+ #if IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
+ unsigned long get_top_of_ram(void)
+ {
+diff --git a/src/southbridge/amd/sb700/lpc.c b/src/southbridge/amd/sb700/lpc.c
+index 0cc1e8b..145a01f 100644
+--- a/src/southbridge/amd/sb700/lpc.c
++++ b/src/southbridge/amd/sb700/lpc.c
+@@ -61,10 +61,12 @@ static void lpc_init(device_t dev)
+ isa_dma_init();
+ #endif
+
+- /* Enable DMA transaction on the LPC bus */
+- byte = pci_read_config8(dev, 0x40);
+- byte |= (1 << 2);
+- pci_write_config8(dev, 0x40, byte);
++ if (!IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA)) {
++ /* Enable DMA transaction on the LPC bus */
++ byte = pci_read_config8(dev, 0x40);
++ byte |= (1 << 2);
++ pci_write_config8(dev, 0x40, byte);
++ }
+
+ /* Disable the timeout mechanism on LPC */
+ byte = pci_read_config8(dev, 0x48);
+@@ -85,11 +87,13 @@ static void lpc_init(device_t dev)
+ cmos_check_update_date();
+ }
+
++#if (!IS_ENABLED(CONFIG_EARLY_CBMEM_INIT))
+ int acpi_get_sleep_type(void)
+ {
+ u16 tmp = inw(ACPI_PM1_CNT_BLK);
+ return ((tmp & (7 << 10)) >> 10);
+ }
++#endif
+
+ #if IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
+ void backup_top_of_ram(uint64_t ramtop)
+diff --git a/src/southbridge/amd/sb700/sb700.h b/src/southbridge/amd/sb700/sb700.h
+index ca020a5..b477091 100644
+--- a/src/southbridge/amd/sb700/sb700.h
++++ b/src/southbridge/amd/sb700/sb700.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -75,6 +76,8 @@ void sb7xx_51xx_setup_sata_phys(struct device *dev);
+
+ #endif
+
++void set_lpc_sticky_ctl(bool enable);
++
+ int s3_save_nvram_early(u32 dword, int size, int nvram_pos);
+ int s3_load_nvram_early(int size, u32 *old_dword, int nvram_pos);
+
+diff --git a/src/southbridge/amd/sb700/spi.c b/src/southbridge/amd/sb700/spi.c
+new file mode 100644
+index 0000000..a38ca2b
+--- /dev/null
++++ b/src/southbridge/amd/sb700/spi.c
+@@ -0,0 +1,148 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2012 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++#include <arch/io.h>
++#include <console/console.h>
++#include <spi-generic.h>
++#include <spi_flash.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pci_ops.h>
++
++#define AMD_SB_SPI_TX_LEN 8
++
++static uint32_t get_spi_bar(void)
++{
++ device_t dev;
++
++ dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
++ return pci_read_config32(dev, 0xa0) & ~0x1f;
++}
++
++void spi_init(void)
++{
++ /* Not needed */
++}
++
++unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
++{
++ return min(AMD_SB_SPI_TX_LEN - cmd_len, buf_len);
++}
++
++static void reset_internal_fifo_pointer(void)
++{
++ uint32_t spibar = get_spi_bar();
++
++ do {
++ write8((void *)(spibar + 2),
++ read8((void *)(spibar + 2)) | 0x10);
++ } while (read8((void *)(spibar + 0xd)) & 0x7);
++}
++
++static void execute_command(void)
++{
++ uint32_t spibar = get_spi_bar();
++
++ write8((void *)(spibar + 2), read8((void *)(spibar + 2)) | 1);
++
++ while ((read8((void *)(spibar + 2)) & 1) &&
++ (read8((void *)(spibar+3)) & 0x80));
++}
++
++int spi_claim_bus(struct spi_slave *slave)
++{
++ /* Handled internally by the SB700 */
++ return 0;
++}
++
++void spi_release_bus(struct spi_slave *slave)
++{
++ /* Handled internally by the SB700 */
++}
++
++struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
++{
++ struct spi_slave *slave = malloc(sizeof(*slave));
++
++ if (!slave) {
++ return NULL;
++ }
++
++ memset(slave, 0, sizeof(*slave));
++
++ return slave;
++}
++
++int spi_xfer(struct spi_slave *slave, const void *dout,
++ unsigned int bytesout, void *din, unsigned int bytesin)
++{
++ /* First byte is cmd which cannot be sent through the FIFO. */
++ u8 cmd = *(u8 *)dout++;
++ u8 readoffby1;
++ u8 readwrite;
++ u8 count;
++
++ uint32_t spibar = get_spi_bar();
++
++ bytesout--;
++
++ /*
++ * Check if this is a write command attempting to transfer more bytes
++ * than the controller can handle. Iterations for writes are not
++ * supported here because each SPI write command needs to be preceded
++ * and followed by other SPI commands, and this sequence is controlled
++ * by the SPI chip driver.
++ */
++ if (bytesout > AMD_SB_SPI_TX_LEN) {
++ printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
++ " spi_crop_chunk()?\n");
++ return -1;
++ }
++
++ readoffby1 = bytesout ? 0 : 1;
++
++ readwrite = (bytesin + readoffby1) << 4 | bytesout;
++ write8((void *)(spibar + 1), readwrite);
++ write8((void *)(spibar + 0), cmd);
++
++ reset_internal_fifo_pointer();
++ for (count = 0; count < bytesout; count++, dout++) {
++ write8((void *)(spibar + 0x0C), *(u8 *)dout);
++ }
++
++ reset_internal_fifo_pointer();
++ execute_command();
++
++ reset_internal_fifo_pointer();
++ /* Skip the bytes we sent. */
++ for (count = 0; count < bytesout; count++) {
++ cmd = read8((void *)(spibar + 0x0C));
++ }
++
++ reset_internal_fifo_pointer();
++ for (count = 0; count < bytesin; count++, din++) {
++ *(u8 *)din = read8((void *)(spibar + 0x0C));
++ }
++
++ return 0;
++}
+\ No newline at end of file
+diff --git a/src/southbridge/amd/sb700/spi.h b/src/southbridge/amd/sb700/spi.h
+new file mode 100644
+index 0000000..9b76b35
+--- /dev/null
++++ b/src/southbridge/amd/sb700/spi.h
+@@ -0,0 +1,21 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++int spi_claim_bus(struct spi_slave *slave);
++void spi_release_bus(struct spi_slave *slave);
+\ No newline at end of file
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0020-superio-nuvoton-nct5572d-Enable-power-state-after-po.patch b/resources/libreboot/patch/kgpe-d16/0020-superio-nuvoton-nct5572d-Enable-power-state-after-po.patch
new file mode 100644
index 00000000..7f55dae3
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0020-superio-nuvoton-nct5572d-Enable-power-state-after-po.patch
@@ -0,0 +1,80 @@
+From 5f367eefa624cdc66127e0effafdc08d68352c4e Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:26:30 -0500
+Subject: [PATCH 020/146] superio/nuvoton/nct5572d: Enable power state after
+ power failure support
+
+---
+ src/superio/nuvoton/nct5572d/superio.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+diff --git a/src/superio/nuvoton/nct5572d/superio.c b/src/superio/nuvoton/nct5572d/superio.c
+index c5278d6..ccf2416 100644
+--- a/src/superio/nuvoton/nct5572d/superio.c
++++ b/src/superio/nuvoton/nct5572d/superio.c
+@@ -3,6 +3,7 @@
+ *
+ * Copyright (C) 2014 Felix Held <felix-coreboot@felixheld.de>
+ * Copyright (C) 2014 Edward O'Callaghan <eocallaghan@alterapraxis.com>
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -19,17 +20,29 @@
+ * Foundation, Inc.
+ */
+
++#include <console/console.h>
+ #include <arch/io.h>
+ #include <device/device.h>
+ #include <device/pnp.h>
+ #include <pc80/keyboard.h>
++#include <pc80/mc146818rtc.h>
+ #include <stdlib.h>
+ #include <superio/conf_mode.h>
+
+ #include "nct5572d.h"
+
++#define MAINBOARD_POWER_OFF 0
++#define MAINBOARD_POWER_ON 1
++
++#ifndef CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL
++#define CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON
++#endif
++
+ static void nct5572d_init(struct device *dev)
+ {
++ uint8_t byte;
++ uint32_t power_status;
++
+ if (!dev->enabled)
+ return;
+
+@@ -38,6 +51,24 @@ static void nct5572d_init(struct device *dev)
+ case NCT5572D_KBC:
+ pc_keyboard_init();
+ break;
++ case NCT5572D_ACPI:
++ /* Set power state after power fail */
++ power_status = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
++ get_option(&power_status, "power_on_after_fail");
++ pnp_enter_conf_mode_8787(dev);
++ outb(0x07, dev->path.pnp.port);
++ outb(NCT5572D_ACPI, dev->path.pnp.port+1); /* ACPI function */
++ outb(0xe4, dev->path.pnp.port); /* CRE4 */
++ byte = inb(dev->path.pnp.port+1);
++ byte &= ~0x60;
++ if (power_status == 1)
++ byte |= (0x1 << 5); /* Force power on */
++ else if (power_status == 2)
++ byte |= (0x2 << 5); /* Use last power state */
++ outb(byte, dev->path.pnp.port+1);
++ pnp_exit_conf_mode_aa(dev);
++ printk(BIOS_INFO, "set power %s after power fail\n", power_status ? "on" : "off");
++ break;
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0021-northbridge-amd-amdfam10-Add-Suspend-to-RAM-S3-Flash.patch b/resources/libreboot/patch/kgpe-d16/0021-northbridge-amd-amdfam10-Add-Suspend-to-RAM-S3-Flash.patch
new file mode 100644
index 00000000..d9b8f347
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0021-northbridge-amd-amdfam10-Add-Suspend-to-RAM-S3-Flash.patch
@@ -0,0 +1,156 @@
+From 9e460817405fe102ad414a71c85ff2b68a8f4469 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:39:34 -0500
+Subject: [PATCH 021/146] northbridge/amd/amdfam10: Add Suspend to RAM (S3)
+ Flash data storage area
+
+---
+ src/northbridge/amd/amdfam10/Kconfig | 6 +++
+ src/northbridge/amd/amdfam10/Makefile.inc | 19 ++++++++++
+ src/northbridge/amd/amdfam10/northbridge.c | 4 ++
+ src/northbridge/amd/amdfam10/raminit_amdmct.c | 50 ++++++++++++++-----------
+ 4 files changed, 57 insertions(+), 22 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/Kconfig b/src/northbridge/amd/amdfam10/Kconfig
+index 4d7147d..ff92fca 100644
+--- a/src/northbridge/amd/amdfam10/Kconfig
++++ b/src/northbridge/amd/amdfam10/Kconfig
+@@ -89,6 +89,12 @@ if DIMM_FBDIMM
+ default 0x0110
+ endif
+
++if HAVE_ACPI_RESUME
++ config S3_DATA_SIZE
++ int
++ default 16384
++endif
++
+ if DIMM_DDR2
+ if DIMM_REGISTERED
+ config DIMM_SUPPORT
+diff --git a/src/northbridge/amd/amdfam10/Makefile.inc b/src/northbridge/amd/amdfam10/Makefile.inc
+index 8a105fd..b4097b4 100644
+--- a/src/northbridge/amd/amdfam10/Makefile.inc
++++ b/src/northbridge/amd/amdfam10/Makefile.inc
+@@ -15,4 +15,23 @@ ramstage-y += get_pci1234.c
+ # Call show_all_routes() anywhere amdfam10.h is included.
+ #ramstage-y += util.c
+
++ifeq ($(CONFIG_HAVE_ACPI_RESUME), y)
++
++$(obj)/coreboot_s3nv.rom: $(obj)/config.h
++ echo " S3 NVRAM $(CONFIG_S3_DATA_POS) (S3 storage area)"
++ # force C locale, so cygwin awk doesn't try to interpret the 0xff below as UTF-8 (or worse)
++ printf %d $(CONFIG_S3_DATA_SIZE) | LC_ALL=C awk '{for (i=0; i<$$1*2; i++) {printf "%c", 255}}' > $@.tmp
++ mv $@.tmp $@
++
++cbfs-files-y += s3nv
++s3nv-file := $(obj)/coreboot_s3nv.rom
++s3nv-position := $(CONFIG_S3_DATA_POS)
++s3nv-type := raw
++
++ifeq ($(CONFIG_DIMM_DDR3), y)
++ramstage-y += ../amdmct/mct_ddr3/s3utils.c
++endif
++
++endif
++
+ endif
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 7bd8675..880129b 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -54,6 +54,8 @@
+ #include <sb_cimx.h>
+ #endif
+
++#include "../amdmct/mct_ddr3/s3utils.h"
++
+ struct amdfam10_sysconf_t sysconf;
+
+ #define FX_DEVS NODE_NUMS
+@@ -1413,6 +1415,8 @@ static void root_complex_enable_dev(struct device *dev)
+ /* Do not delay UMA setup, as a device on the PCI bus may evaluate
+ the global uma_memory variables already in its enable function. */
+ if (!done) {
++ save_mct_information_to_nvram();
++
+ setup_bsp_ramtop();
+ setup_uma_memory();
+ done = 1;
+diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+index fa14e4f..9c2612c 100644
+--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
++++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+@@ -101,6 +101,10 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq
+ #include "../amdmct/mct_ddr3/mct_d.h"
+ #include "../amdmct/mct_ddr3/mct_d_gcc.h"
+
++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
++#include "../amdmct/mct_ddr3/s3utils.c"
++#endif
++
+ #include "../amdmct/wrappers/mcti_d.c"
+ #include "../amdmct/mct_ddr3/mct_d.c"
+
+@@ -240,33 +244,35 @@ static void amdmct_cbmem_store_info(struct sys_info *sysinfo)
+ size_t i;
+ struct DCTStatStruc *pDCTstatA = NULL;
+
+- /* Allocate memory */
+- struct amdmct_memory_info* mem_info;
+- mem_info = cbmem_add(CBMEM_ID_AMDMCT_MEMINFO, sizeof(struct amdmct_memory_info));
+- if (!mem_info)
+- return;
++ if (!acpi_is_wakeup_s3()) {
++ /* Allocate memory */
++ struct amdmct_memory_info* mem_info;
++ mem_info = cbmem_add(CBMEM_ID_AMDMCT_MEMINFO, sizeof(struct amdmct_memory_info));
++ if (!mem_info)
++ return;
+
+- printk(BIOS_DEBUG, "%s: Storing AMDMCT configuration in CBMEM\n", __func__);
++ printk(BIOS_DEBUG, "%s: Storing AMDMCT configuration in CBMEM\n", __func__);
+
+- /* Initialize memory */
+- memset(mem_info, 0, sizeof(struct amdmct_memory_info));
++ /* Initialize memory */
++ memset(mem_info, 0, sizeof(struct amdmct_memory_info));
+
+- /* Copy data */
+- memcpy(&mem_info->mct_stat, &(sysinfo->MCTstat), sizeof(struct MCTStatStruc));
+- for (i = 0; i < MAX_NODES_SUPPORTED; i++) {
+- pDCTstatA = sysinfo->DCTstatA + i;
+- memcpy(&mem_info->dct_stat[i], pDCTstatA, sizeof(struct DCTStatStruc));
+- }
+- mem_info->ecc_enabled = mctGet_NVbits(NV_ECC_CAP);
+- mem_info->ecc_scrub_rate = mctGet_NVbits(NV_DramBKScrub);
++ /* Copy data */
++ memcpy(&mem_info->mct_stat, &(sysinfo->MCTstat), sizeof(struct MCTStatStruc));
++ for (i = 0; i < MAX_NODES_SUPPORTED; i++) {
++ pDCTstatA = sysinfo->DCTstatA + i;
++ memcpy(&mem_info->dct_stat[i], pDCTstatA, sizeof(struct DCTStatStruc));
++ }
++ mem_info->ecc_enabled = mctGet_NVbits(NV_ECC_CAP);
++ mem_info->ecc_scrub_rate = mctGet_NVbits(NV_DramBKScrub);
+
+- /* Zero out invalid/unused pointers */
++ /* Zero out invalid/unused pointers */
+ #if IS_ENABLED(CONFIG_DIMM_DDR3)
+- for (i = 0; i < MAX_NODES_SUPPORTED; i++) {
+- mem_info->dct_stat[i].C_MCTPtr = NULL;
+- mem_info->dct_stat[i].C_DCTPtr[0] = NULL;
+- mem_info->dct_stat[i].C_DCTPtr[1] = NULL;
+- }
++ for (i = 0; i < MAX_NODES_SUPPORTED; i++) {
++ mem_info->dct_stat[i].C_MCTPtr = NULL;
++ mem_info->dct_stat[i].C_DCTPtr[0] = NULL;
++ mem_info->dct_stat[i].C_DCTPtr[1] = NULL;
++ }
+ #endif
++ }
+ }
+ #endif
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0022-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch b/resources/libreboot/patch/kgpe-d16/0022-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch
new file mode 100644
index 00000000..de64998e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0022-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch
@@ -0,0 +1,986 @@
+From 2a7645f564b30a7914b8036c1b6919c9ffcbf9a2 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:40:31 -0500
+Subject: [PATCH 022/146] northbridge/amd/amdmct/mct_ddr3: Add initial Suspend
+ to RAM (S3) support
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 166 +++----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 112 +++++
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 612 +++++++++++++++++++++++++
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.h | 28 ++
+ 4 files changed, 839 insertions(+), 79 deletions(-)
+ create mode 100644 src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+ create mode 100644 src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index fa59d71..0c06444 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -272,92 +272,100 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ u8 Node, NodesWmem;
+ u32 node_sys_base;
+
++ uint8_t s3resume = acpi_is_wakeup_s3();
++
+ restartinit:
+ mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/
+- NodesWmem = 0;
+- node_sys_base = 0;
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
+-
+- /* Zero out data structures to avoid false detection of DIMMs */
+- memset(pDCTstat, 0, sizeof(struct DCTStatStruc));
+-
+- /* Initialize data structures */
+- pDCTstat->Node_ID = Node;
+- pDCTstat->dev_host = PA_HOST(Node);
+- pDCTstat->dev_map = PA_MAP(Node);
+- pDCTstat->dev_dct = PA_DCT(Node);
+- pDCTstat->dev_nbmisc = PA_NBMISC(Node);
+- pDCTstat->NodeSysBase = node_sys_base;
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
+- mct_init(pMCTstat, pDCTstat);
+- mctNodeIDDebugPort_D();
+- pDCTstat->NodePresent = NodePresent_D(Node);
+- if (pDCTstat->NodePresent) { /* See if Node is there*/
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n");
+- clear_legacy_Mode(pMCTstat, pDCTstat);
+- pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n");
+- mct_InitialMCT_D(pMCTstat, pDCTstat);
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+- mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n");
+- mct_initDCT(pMCTstat, pDCTstat);
+- if (pDCTstat->ErrCode == SC_FatalErr) {
+- goto fatalexit; /* any fatal errors?*/
+- } else if (pDCTstat->ErrCode < SC_StopError) {
+- NodesWmem++;
+- }
+- } /* if Node present */
+- node_sys_base = pDCTstat->NodeSysBase;
+- node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
+- }
+- if (NodesWmem == 0) {
+- printk(BIOS_DEBUG, "No Nodes?!\n");
+- goto fatalexit;
+- }
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+- SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n");
+- HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
+- mctHookAfterHTMap();
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
+- CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
+- mctHookAfterCPU(); /* Setup external northbridge(s) */
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+- DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+- UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+- mct_OtherTiming(pMCTstat, pDCTstatA);
++ if (s3resume) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n");
++ restore_mct_information_from_nvram();
++ } else {
++ NodesWmem = 0;
++ node_sys_base = 0;
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ /* Zero out data structures to avoid false detection of DIMMs */
++ memset(pDCTstat, 0, sizeof(struct DCTStatStruc));
++
++ /* Initialize data structures */
++ pDCTstat->Node_ID = Node;
++ pDCTstat->dev_host = PA_HOST(Node);
++ pDCTstat->dev_map = PA_MAP(Node);
++ pDCTstat->dev_dct = PA_DCT(Node);
++ pDCTstat->dev_nbmisc = PA_NBMISC(Node);
++ pDCTstat->NodeSysBase = node_sys_base;
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
++ mct_init(pMCTstat, pDCTstat);
++ mctNodeIDDebugPort_D();
++ pDCTstat->NodePresent = NodePresent_D(Node);
++ if (pDCTstat->NodePresent) { /* See if Node is there*/
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n");
++ clear_legacy_Mode(pMCTstat, pDCTstat);
++ pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n");
++ mct_InitialMCT_D(pMCTstat, pDCTstat);
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n");
++ mct_initDCT(pMCTstat, pDCTstat);
++ if (pDCTstat->ErrCode == SC_FatalErr) {
++ goto fatalexit; /* any fatal errors?*/
++ } else if (pDCTstat->ErrCode < SC_StopError) {
++ NodesWmem++;
++ }
++ } /* if Node present */
++ node_sys_base = pDCTstat->NodeSysBase;
++ node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
++ }
++ if (NodesWmem == 0) {
++ printk(BIOS_DEBUG, "No Nodes?!\n");
++ goto fatalexit;
++ }
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
++ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n");
++ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
++ mctHookAfterHTMap();
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++ mctHookAfterCPU(); /* Setup external northbridge(s) */
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
++ DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
++ mct_OtherTiming(pMCTstat, pDCTstatA);
++
++
++ if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
++ goto restartinit;
++ }
+
+- if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
+- goto restartinit;
+- }
++ InterleaveNodes_D(pMCTstat, pDCTstatA);
++ InterleaveChannels_D(pMCTstat, pDCTstatA);
+
+- InterleaveNodes_D(pMCTstat, pDCTstatA);
+- InterleaveChannels_D(pMCTstat, pDCTstatA);
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
++ if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
++ MCTMemClr_D(pMCTstat,pDCTstatA);
++ }
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+- if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+- MCTMemClr_D(pMCTstat,pDCTstatA);
++ mct_FinalMCT_D(pMCTstat, pDCTstatA);
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
+ }
+
+- mct_FinalMCT_D(pMCTstat, pDCTstatA);
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
+-
+ return;
+
+ fatalexit:
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 219aa42..c790d7e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -24,6 +24,8 @@
+ #ifndef MCT_D_H
+ #define MCT_D_H
+
++#include <cpu/x86/msr.h>
++
+ /*===========================================================================
+ CPU - K8/FAM10
+ ===========================================================================*/
+@@ -596,6 +598,116 @@ struct DCTStatStruc { /* A per Node structure*/
+ uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
+ } __attribute__((packed));
+
++struct amd_s3_persistent_mct_channel_data {
++ /* Stage 1 (1 dword) */
++ uint32_t f2x110;
++
++ /* Stage 2 (88 dwords) */
++ uint32_t f1x40;
++ uint32_t f1x44;
++ uint32_t f1x48;
++ uint32_t f1x4c;
++ uint32_t f1x50;
++ uint32_t f1x54;
++ uint32_t f1x58;
++ uint32_t f1x5c;
++ uint32_t f1x60;
++ uint32_t f1x64;
++ uint32_t f1x68;
++ uint32_t f1x6c;
++ uint32_t f1x70;
++ uint32_t f1x74;
++ uint32_t f1x78;
++ uint32_t f1x7c;
++ uint32_t f1xf0;
++ uint32_t f1x120;
++ uint32_t f1x124;
++ uint32_t f2x10c;
++ uint32_t f2x114;
++ uint32_t f2x118;
++ uint32_t f2x11c;
++ uint32_t f2x1b0;
++ uint32_t f3x44;
++ uint64_t msr0000020[16];
++ uint64_t msr00000250;
++ uint64_t msr00000258;
++ uint64_t msr0000026[8];
++ uint64_t msr000002ff;
++ uint64_t msrc0010010;
++ uint64_t msrc001001a;
++ uint64_t msrc001001d;
++ uint64_t msrc001001f;
++
++ /* Stage 3 (21 dwords) */
++ uint32_t f2x40;
++ uint32_t f2x44;
++ uint32_t f2x48;
++ uint32_t f2x4c;
++ uint32_t f2x50;
++ uint32_t f2x54;
++ uint32_t f2x58;
++ uint32_t f2x5c;
++ uint32_t f2x60;
++ uint32_t f2x64;
++ uint32_t f2x68;
++ uint32_t f2x6c;
++ uint32_t f2x78;
++ uint32_t f2x7c;
++ uint32_t f2x80;
++ uint32_t f2x84;
++ uint32_t f2x88;
++ uint32_t f2x8c;
++ uint32_t f2x90;
++ uint32_t f2xa4;
++ uint32_t f2xa8;
++
++ /* Stage 4 (1 dword) */
++ uint32_t f2x94;
++
++ /* Stage 6 (33 dwords) */
++ uint32_t f2x9cx0d0f0_f_8_0_0_8_4_0[9][3]; /* [lane][setting] */
++ uint32_t f2x9cx00;
++ uint32_t f2x9cx0a;
++ uint32_t f2x9cx0c;
++
++ /* Stage 7 (1 dword) */
++ uint32_t f2x9cx04;
++
++ /* Stage 9 (2 dwords) */
++ uint32_t f2x9cx0d0fe006;
++ uint32_t f2x9cx0d0fe007;
++
++ /* Stage 10 (78 dwords) */
++ uint32_t f2x9cx10[12];
++ uint32_t f2x9cx20[12];
++ uint32_t f2x9cx3_0_0_3_1[4][3]; /* [dimm][setting] */
++ uint32_t f2x9cx3_0_0_7_5[4][3]; /* [dimm][setting] */
++ uint32_t f2x9cx0d;
++ uint32_t f2x9cx0d0f0_f_0_13[9]; /* [lane] */
++ uint32_t f2x9cx0d0f0_f_0_30[9]; /* [lane] */
++ uint32_t f2x9cx0d0f2_f_0_30[4]; /* [pad select] */
++ uint32_t f2x9cx0d0f8_8_4_0[2][3]; /* [offset][pad select] */
++ uint32_t f2x9cx0d0f812f;
++
++ /* Stage 11 (24 dwords) */
++ uint32_t f2x9cx30[12];
++ uint32_t f2x9cx40[12];
++
++ /* Other (1 dword) */
++ uint32_t f3x58;
++
++ /* TOTAL: 250 dwords */
++} __attribute__((packed));
++
++struct amd_s3_persistent_node_data {
++ uint32_t node_present;
++ struct amd_s3_persistent_mct_channel_data channel[2];
++} __attribute__((packed));
++
++struct amd_s3_persistent_data {
++ struct amd_s3_persistent_node_data node[MAX_NODES_SUPPORTED];
++} __attribute__((packed));
++
+ /*===============================================================================
+ Local Error Status Codes (DCTStatStruc.ErrCode)
+ ===============================================================================*/
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+new file mode 100644
+index 0000000..79576d6
+--- /dev/null
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -0,0 +1,612 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <string.h>
++#include <arch/acpi.h>
++#include <cpu/x86/msr.h>
++#include <device/device.h>
++#include <device/pci_def.h>
++#include <device/pci_ops.h>
++#include <console/console.h>
++#include <cbfs.h>
++#include <spi-generic.h>
++#include <spi_flash.h>
++
++#include "s3utils.h"
++
++#define S3NV_FILE_NAME "s3nv"
++
++static ssize_t get_s3nv_file_offset(void);
++
++ssize_t get_s3nv_file_offset(void)
++{
++ struct cbfs_file file;
++ ssize_t offset;
++ struct cbfs_media *media = CBFS_DEFAULT_MEDIA;
++
++ offset = cbfs_locate_file(media, &file, S3NV_FILE_NAME);
++ if (offset < 0) {
++ printk(BIOS_DEBUG, "S3 state file not found in CBFS: %s\n", S3NV_FILE_NAME);
++ return -1;
++ }
++
++ return offset;
++}
++
++static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
++{
++ uint32_t dword;
++
++ index &= ~(1 << 30);
++ pci_write_config32(dev, index_ctl_reg, index);
++ do {
++ dword = pci_read_config32(dev, index_ctl_reg);
++ } while (!(dword & (1 << 31)));
++ dword = pci_read_config32(dev, index_ctl_reg + 0x04);
++
++ return dword;
++}
++
++#ifdef __RAMSTAGE__
++static uint64_t rdmsr_uint64_t(unsigned long index) {
++ msr_t msr = rdmsr(index);
++ return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo);
++}
++
++void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
++{
++ uint8_t i;
++ uint8_t j;
++ uint8_t node;
++ uint8_t channel;
++
++ /* Zero out data structure */
++ memset(persistent_data, 0, sizeof(struct amd_s3_persistent_data));
++
++ /* Load data from DCTs into data structure */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++ device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2));
++ device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3));
++ if ((!dev_fn1) || (!dev_fn2) || (!dev_fn3)) {
++ persistent_data->node[node].node_present = 0;
++ continue;
++ }
++ persistent_data->node[node].node_present = 1;
++
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++
++ /* Stage 1 */
++ data->f2x110 = pci_read_config32(dev_fn2, 0x110);
++
++ /* Stage 2 */
++ data->f1x40 = pci_read_config32(dev_fn1, 0x40 + (0x100 * channel));
++ data->f1x44 = pci_read_config32(dev_fn1, 0x44 + (0x100 * channel));
++ data->f1x48 = pci_read_config32(dev_fn1, 0x48 + (0x100 * channel));
++ data->f1x4c = pci_read_config32(dev_fn1, 0x4c + (0x100 * channel));
++ data->f1x50 = pci_read_config32(dev_fn1, 0x50 + (0x100 * channel));
++ data->f1x54 = pci_read_config32(dev_fn1, 0x54 + (0x100 * channel));
++ data->f1x58 = pci_read_config32(dev_fn1, 0x58 + (0x100 * channel));
++ data->f1x5c = pci_read_config32(dev_fn1, 0x5c + (0x100 * channel));
++ data->f1x60 = pci_read_config32(dev_fn1, 0x60 + (0x100 * channel));
++ data->f1x64 = pci_read_config32(dev_fn1, 0x64 + (0x100 * channel));
++ data->f1x68 = pci_read_config32(dev_fn1, 0x68 + (0x100 * channel));
++ data->f1x6c = pci_read_config32(dev_fn1, 0x6c + (0x100 * channel));
++ data->f1x70 = pci_read_config32(dev_fn1, 0x70 + (0x100 * channel));
++ data->f1x74 = pci_read_config32(dev_fn1, 0x74 + (0x100 * channel));
++ data->f1x78 = pci_read_config32(dev_fn1, 0x78 + (0x100 * channel));
++ data->f1x7c = pci_read_config32(dev_fn1, 0x7c + (0x100 * channel));
++ data->f1xf0 = pci_read_config32(dev_fn1, 0xf0);
++ data->f1x120 = pci_read_config32(dev_fn1, 0x120);
++ data->f1x124 = pci_read_config32(dev_fn1, 0x124);
++ data->f2x10c = pci_read_config32(dev_fn2, 0x10c);
++ data->f2x114 = pci_read_config32(dev_fn2, 0x114);
++ data->f2x118 = pci_read_config32(dev_fn2, 0x118);
++ data->f2x11c = pci_read_config32(dev_fn2, 0x11c);
++ data->f2x1b0 = pci_read_config32(dev_fn2, 0x1b0);
++ data->f3x44 = pci_read_config32(dev_fn3, 0x44);
++ for (i=0; i<16; i++) {
++ data->msr0000020[i] = rdmsr_uint64_t(0x00000200 | i);
++ }
++ data->msr00000250 = rdmsr_uint64_t(0x00000250);
++ data->msr00000258 = rdmsr_uint64_t(0x00000258);
++ for (i=0; i<8; i++)
++ data->msr0000026[i] = rdmsr_uint64_t(0x00000260 | (i + 8));
++ data->msr000002ff = rdmsr_uint64_t(0x000002ff);
++ data->msrc0010010 = rdmsr_uint64_t(0xc0010010);
++ data->msrc001001a = rdmsr_uint64_t(0xc001001a);
++ data->msrc001001d = rdmsr_uint64_t(0xc001001d);
++ data->msrc001001f = rdmsr_uint64_t(0xc001001f);
++
++ /* Stage 3 */
++ data->f2x40 = pci_read_config32(dev_fn2, 0x40 + (0x100 * channel));
++ data->f2x44 = pci_read_config32(dev_fn2, 0x44 + (0x100 * channel));
++ data->f2x48 = pci_read_config32(dev_fn2, 0x48 + (0x100 * channel));
++ data->f2x4c = pci_read_config32(dev_fn2, 0x4c + (0x100 * channel));
++ data->f2x50 = pci_read_config32(dev_fn2, 0x50 + (0x100 * channel));
++ data->f2x54 = pci_read_config32(dev_fn2, 0x54 + (0x100 * channel));
++ data->f2x58 = pci_read_config32(dev_fn2, 0x58 + (0x100 * channel));
++ data->f2x5c = pci_read_config32(dev_fn2, 0x5c + (0x100 * channel));
++ data->f2x60 = pci_read_config32(dev_fn2, 0x60 + (0x100 * channel));
++ data->f2x64 = pci_read_config32(dev_fn2, 0x64 + (0x100 * channel));
++ data->f2x68 = pci_read_config32(dev_fn2, 0x68 + (0x100 * channel));
++ data->f2x6c = pci_read_config32(dev_fn2, 0x6c + (0x100 * channel));
++ data->f2x78 = pci_read_config32(dev_fn2, 0x78 + (0x100 * channel));
++ data->f2x7c = pci_read_config32(dev_fn2, 0x7c + (0x100 * channel));
++ data->f2x80 = pci_read_config32(dev_fn2, 0x80 + (0x100 * channel));
++ data->f2x84 = pci_read_config32(dev_fn2, 0x84 + (0x100 * channel));
++ data->f2x88 = pci_read_config32(dev_fn2, 0x88 + (0x100 * channel));
++ data->f2x8c = pci_read_config32(dev_fn2, 0x8c + (0x100 * channel));
++ data->f2x90 = pci_read_config32(dev_fn2, 0x90 + (0x100 * channel));
++ data->f2xa4 = pci_read_config32(dev_fn2, 0xa4 + (0x100 * channel));
++ data->f2xa8 = pci_read_config32(dev_fn2, 0xa8 + (0x100 * channel));
++
++ /* Stage 4 */
++ data->f2x94 = pci_read_config32(dev_fn2, 0x94 + (0x100 * channel));
++
++ /* Stage 6 */
++ for (i=0; i<9; i++)
++ for (j=0; j<3; j++)
++ data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
++ data->f2x9cx00 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x00);
++ data->f2x9cx0a = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0a);
++ data->f2x9cx0c = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0c);
++
++ /* Stage 7 */
++ data->f2x9cx04 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x04);
++
++ /* Stage 9 */
++ data->f2x9cx0d0fe006 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe006);
++ data->f2x9cx0d0fe007 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe007);
++
++ /* Stage 10 */
++ for (i=0; i<12; i++)
++ data->f2x9cx10[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x10 + i);
++ for (i=0; i<12; i++)
++ data->f2x9cx20[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x20 + i);
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j));
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j));
++ data->f2x9cx0d = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d);
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8));
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8));
++ for (i=0; i<4; i++)
++ data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8));
++ for (i=0; i<2; i++)
++ for (j=0; j<3; j++)
++ data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
++ data->f2x9cx0d0f812f = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f812f);
++
++ /* Stage 11 */
++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ for (i=0; i<12; i++)
++ data->f2x9cx30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x30 + i);
++ for (i=0; i<12; i++)
++ data->f2x9cx40[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x40 + i);
++ }
++
++ /* Other */
++ /* ECC scrub rate control */
++ data->f3x58 = pci_read_config32(dev_fn3, 0x58);
++ }
++ }
++}
++#else
++static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
++{
++ uint32_t dword;
++
++ pci_write_config32(dev, index_ctl_reg + 0x04, value);
++ index |= (1 << 30);
++ pci_write_config32(dev, index_ctl_reg, index);
++ do {
++ dword = pci_read_config32(dev, index_ctl_reg);
++ } while (!(dword & (1 << 31)));
++}
++#endif
++
++#ifdef __PRE_RAM__
++static void wrmsr_uint64_t(unsigned long index, uint64_t value) {
++ msr_t msr;
++ msr.hi = (value & 0xffffffff00000000ULL) >> 32;
++ msr.lo = (value & 0xffffffff);
++ wrmsr(index, msr);
++}
++
++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data)
++{
++ uint8_t i;
++ uint8_t j;
++ uint8_t node;
++ uint8_t channel;
++ uint8_t ganged;
++ uint8_t dct_enabled;
++ uint32_t dword;
++
++ /* Load data from data structure into DCTs */
++ /* Stage 1 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x110, data->f2x110);
++ }
++ }
++
++ /* Stage 2 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x40 + (0x100 * channel), data->f1x40);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x44 + (0x100 * channel), data->f1x44);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x48 + (0x100 * channel), data->f1x48);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x4c + (0x100 * channel), data->f1x4c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x50 + (0x100 * channel), data->f1x50);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x54 + (0x100 * channel), data->f1x54);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x58 + (0x100 * channel), data->f1x58);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x5c + (0x100 * channel), data->f1x5c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x60 + (0x100 * channel), data->f1x60);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x64 + (0x100 * channel), data->f1x64);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x68 + (0x100 * channel), data->f1x68);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x6c + (0x100 * channel), data->f1x6c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x70 + (0x100 * channel), data->f1x70);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x74 + (0x100 * channel), data->f1x74);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x78 + (0x100 * channel), data->f1x78);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x7c + (0x100 * channel), data->f1x7c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0xf0 + (0x100 * channel), data->f1xf0);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x120 + (0x100 * channel), data->f1x120);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x124 + (0x100 * channel), data->f1x124);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x10c + (0x100 * channel), data->f2x10c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x114 + (0x100 * channel), data->f2x114);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x118 + (0x100 * channel), data->f2x118);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x11c + (0x100 * channel), data->f2x11c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x1b0 + (0x100 * channel), data->f2x1b0);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x44 + (0x100 * channel), data->f3x44);
++ for (i=0; i<16; i++) {
++ wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]);
++ }
++ wrmsr_uint64_t(0x00000250, data->msr00000250);
++ wrmsr_uint64_t(0x00000258, data->msr00000258);
++ /* FIXME
++ * Restoring these MSRs causes a hang on resume
++ * For now, skip restoration...
++ */
++ // for (i=0; i<8; i++)
++ // wrmsr_uint64_t(0x00000260 | (i + 8), data->msr0000026[i]);
++ wrmsr_uint64_t(0x000002ff, data->msr000002ff);
++ wrmsr_uint64_t(0xc0010010, data->msrc0010010);
++ wrmsr_uint64_t(0xc001001a, data->msrc001001a);
++ wrmsr_uint64_t(0xc001001d, data->msrc001001d);
++ wrmsr_uint64_t(0xc001001f, data->msrc001001f);
++ }
++ }
++
++ /* Stage 3 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ ganged = !!(data->f2x110 & 0x10);
++ if ((ganged == 1) && (channel > 0))
++ continue;
++
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x40 + (0x100 * channel), data->f2x40);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x44 + (0x100 * channel), data->f2x44);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x48 + (0x100 * channel), data->f2x48);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x4c + (0x100 * channel), data->f2x4c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x50 + (0x100 * channel), data->f2x50);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x54 + (0x100 * channel), data->f2x54);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x58 + (0x100 * channel), data->f2x58);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x5c + (0x100 * channel), data->f2x5c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x60 + (0x100 * channel), data->f2x60);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x64 + (0x100 * channel), data->f2x64);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x68 + (0x100 * channel), data->f2x68);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x6c + (0x100 * channel), data->f2x6c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x78 + (0x100 * channel), data->f2x78);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x7c + (0x100 * channel), data->f2x7c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x80 + (0x100 * channel), data->f2x80);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x84 + (0x100 * channel), data->f2x84);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x88 + (0x100 * channel), data->f2x88);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x8c + (0x100 * channel), data->f2x8c);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), data->f2x90);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa4 + (0x100 * channel), data->f2xa4);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa8 + (0x100 * channel), data->f2xa8);
++ }
++ }
++
++ /* Stage 4 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ ganged = !!(data->f2x110 & 0x10);
++ if ((ganged == 1) && (channel > 0))
++ continue;
++
++ /* Disable PHY auto-compensation engine */
++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
++ if (!(dword & (1 << 30))) {
++ dword |= (1 << 30);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
++
++ /* Wait for 5us */
++ mct_Wait(100);
++ }
++
++ /* Restore DRAM Configuration High Register */
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x94 + (0x100 * channel), data->f2x94);
++
++ /* Enable PHY auto-compensation engine */
++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
++ dword &= ~(1 << 30);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
++ }
++ }
++
++ /* Wait for 750us */
++ mct_Wait(15000);
++
++ /* Stage 5 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ ganged = !!(data->f2x110 & 0x10);
++ if ((ganged == 1) && (channel > 0))
++ continue;
++
++ /* Wait for any pending PHY frequency changes to complete */
++ do {
++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
++ } while (dword & (1 << 21));
++ }
++ }
++
++ /* Stage 6 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ for (i=0; i<9; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x00, data->f2x9cx00);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0a, data->f2x9cx0a);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0c, data->f2x9cx0c);
++ }
++ }
++
++ /* Stage 7 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ ganged = !!(data->f2x110 & 0x10);
++ if ((ganged == 1) && (channel > 0))
++ continue;
++
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x04, data->f2x9cx04);
++ }
++ }
++
++ /* Stage 8 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ dct_enabled = !(data->f2x94 & (1 << 14));
++ if (!dct_enabled)
++ continue;
++
++ ganged = !!(data->f2x110 & 0x10);
++ if ((ganged == 1) && (channel > 0))
++ continue;
++
++ printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel);
++
++ /* Exit self refresh mode */
++ dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
++ dword |= (1 << 1);
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), dword);
++ }
++ }
++
++ /* Stage 9 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ dct_enabled = !(data->f2x94 & (1 << 14));
++ if (!dct_enabled)
++ continue;
++
++ printk(BIOS_SPEW, "Waiting for DIMMs to exit self refresh node: %d channel: %d\n", node, channel);
++
++ /* Wait for transition from self refresh mode to complete */
++ do {
++ dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
++ } while (dword & (1 << 1));
++
++ /* Restore registers */
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe006, data->f2x9cx0d0fe006);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe007, data->f2x9cx0d0fe007);
++ }
++ }
++
++ /* Stage 10 */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x10 + i, data->f2x9cx10[i]);
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x20 + i, data->f2x9cx20[i]);
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d, data->f2x9cx0d);
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
++ for (i=0; i<4; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
++ for (i=0; i<2; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]);
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f812f, data->f2x9cx0d0f812f);
++ }
++ }
++
++ /* Stage 11 */
++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x30 + i, data->f2x9cx30[i]);
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x40 + i, data->f2x9cx40[i]);
++ }
++ }
++ }
++
++ /* Other */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ /* ECC scrub rate control */
++ pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58);
++ }
++ }
++}
++#endif
++
++#ifdef __RAMSTAGE__
++int8_t save_mct_information_to_nvram(void)
++{
++ if (acpi_is_wakeup_s3())
++ return 0;
++
++ printk(BIOS_DEBUG, "Writing AMD DCT configuration to Flash\n");
++
++ struct spi_flash *flash;
++ ssize_t s3nv_offset;
++ struct amd_s3_persistent_data persistent_data;
++
++ /* Obtain MCT configuration data */
++ copy_mct_data_to_save_variable(&persistent_data);
++
++ /* Obtain CBFS file offset */
++ s3nv_offset = get_s3nv_file_offset();
++ if (s3nv_offset == -1)
++ return -1;
++
++ /* Align flash pointer to nearest boundary */
++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
++ s3nv_offset += CONFIG_S3_DATA_SIZE;
++
++ /* Set temporary SPI MMIO address */
++ device_t lpc_dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
++ uint32_t spi_mmio_prev = pci_read_config32(lpc_dev, 0xa0);
++ pci_write_config32(lpc_dev, 0xa0, (spi_mmio_prev & 0x1f) | 0xf0000000);
++
++ /* Initialize SPI and detect devices */
++ spi_init();
++ flash = spi_flash_probe(0, 0);
++ if (!flash) {
++ printk(BIOS_DEBUG, "Could not find SPI device\n");
++ return -1;
++ }
++
++ /* Set up SPI flash access */
++ flash->spi->rw = SPI_WRITE_FLAG;
++ spi_claim_bus(flash->spi);
++
++ /* Erase and write data structure */
++ flash->erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
++ flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), &persistent_data);
++
++ /* Tear down SPI flash access */
++ flash->spi->rw = SPI_WRITE_FLAG;
++ spi_release_bus(flash->spi);
++
++ /* Restore SPI MMIO address */
++ pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev);
++
++ return 0;
++}
++#endif
++
++int8_t restore_mct_information_from_nvram(void)
++{
++ ssize_t s3nv_offset;
++ struct amd_s3_persistent_data persistent_data;
++
++ /* Obtain CBFS file offset */
++ s3nv_offset = get_s3nv_file_offset();
++ if (s3nv_offset == -1)
++ return -1;
++
++ /* Align flash pointer to nearest boundary */
++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
++ s3nv_offset += CONFIG_S3_DATA_SIZE;
++
++ cbfs_read(CBFS_DEFAULT_MEDIA, &persistent_data, s3nv_offset, sizeof(struct amd_s3_persistent_data));
++ restore_mct_data_from_save_variable(&persistent_data);
++
++ return 0;
++}
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+new file mode 100644
+index 0000000..dcddcad
+--- /dev/null
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+@@ -0,0 +1,28 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include "../wrappers/mcti.h"
++#include "mct_d.h"
++
++#ifdef __RAMSTAGE__
++int8_t save_mct_information_to_nvram(void);
++#endif
++int8_t restore_mct_information_from_nvram(void);
++void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data);
++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data);
+\ No newline at end of file
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0023-cpu-amd-car-Add-initial-Suspend-to-RAM-S3-support.patch b/resources/libreboot/patch/kgpe-d16/0023-cpu-amd-car-Add-initial-Suspend-to-RAM-S3-support.patch
new file mode 100644
index 00000000..163bd7a5
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0023-cpu-amd-car-Add-initial-Suspend-to-RAM-S3-support.patch
@@ -0,0 +1,54 @@
+From 9ce37d36d63d39880fb2ff50823d087c93e3d7d3 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:46:24 -0500
+Subject: [PATCH 023/146] cpu/amd/car: Add initial Suspend to RAM (S3) support
+
+---
+ src/cpu/amd/car/post_cache_as_ram.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index 230d1aa..e265de1 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -1,4 +1,5 @@
+ /* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2012 ChromeOS Authors
+ * 2005.6 by yhlu
+ * 2006.3 yhlu add copy data from CAR to ram
+ */
+@@ -9,6 +10,7 @@
+ #include <cpu/amd/mtrr.h>
+ #include <cpu/amd/car.h>
+ #include <arch/acpi.h>
++#include <romstage_handoff.h>
+ #include "cbmem.h"
+ #include "cpu/amd/car/disable_cache_as_ram.c"
+
+@@ -103,6 +105,13 @@ void post_cache_as_ram(void)
+ {
+ void *resume_backup_memory = NULL;
+
++ struct romstage_handoff *handoff;
++ handoff = romstage_handoff_find_or_add();
++ if (handoff != NULL)
++ handoff->s3_resume = acpi_is_wakeup_s3();
++ else
++ printk(BIOS_DEBUG, "Romstage handoff structure not added!\n");
++
+ int s3resume = acpi_is_wakeup_s3();
+ if (s3resume) {
+ cbmem_recovery(s3resume);
+@@ -150,6 +159,9 @@ void cache_as_ram_new_stack (void)
+
+ if (acpi_is_wakeup_s3()) {
+ resume_backup_memory = cbmem_find(CBMEM_ID_RESUME);
++#if PRINTK_IN_CAR
++ printk(BIOS_DEBUG, "Resume backup memory location: %p\n", resume_backup_memory);
++#endif
+ }
+ prepare_ramstage_region(resume_backup_memory);
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0024-mainboard-asus-kgpe-d16-Add-initial-Suspend-to-RAM-S.patch b/resources/libreboot/patch/kgpe-d16/0024-mainboard-asus-kgpe-d16-Add-initial-Suspend-to-RAM-S.patch
new file mode 100644
index 00000000..711736e0
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0024-mainboard-asus-kgpe-d16-Add-initial-Suspend-to-RAM-S.patch
@@ -0,0 +1,830 @@
+From f8fce40052840f6c876cfdc85caaec91de977a38 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 7 May 2015 01:32:08 -0500
+Subject: [PATCH 024/146] mainboard/asus/kgpe-d16: Add initial Suspend to RAM
+ (S3) support
+
+---
+ src/mainboard/asus/kgpe-d16/Kconfig | 3 +
+ src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl | 639 +++++++++++++-------------
+ src/mainboard/asus/kgpe-d16/dsdt.asl | 5 +-
+ src/mainboard/asus/kgpe-d16/romstage.c | 34 +-
+ src/mainboard/asus/kgpe-d16/spd_notes.txt | 16 +
+ 5 files changed, 369 insertions(+), 328 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index 95b3b5b..f9556fc 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -23,6 +23,9 @@ config BOARD_SPECIFIC_OPTIONS # dummy
+ select BOARD_ROMSIZE_KB_2048
+ select ENABLE_APIC_EXT_ID
+ select MMCONF_SUPPORT_DEFAULT
++ select SPI_FLASH
++ select SPI_FLASH_WINBOND
++ select HAVE_ACPI_RESUME
+ select DRIVERS_I2C_W83795
+ select DRIVERS_ASPEED_AST2050
+ select MAINBOARD_HAS_NATIVE_VGA_INIT_TEXTMODECFG
+diff --git a/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl b/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
+index b3c65ca..737d8cd 100644
+--- a/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
++++ b/src/mainboard/asus/kgpe-d16/acpi/pm_ctrl.asl
+@@ -18,350 +18,359 @@
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
++/* Port 80 POST card debug */
++OperationRegion (DBG0, SystemIO, 0x80, One)
++ Field (DBG0, ByteAcc, NoLock, Preserve) {
++ DBG8, 8
++}
++
++/* SuperIO control port */
++Name (SPIO, 0x2E)
++
++/* SuperIO control map */
++OperationRegion (SPIM, SystemIO, SPIO, 0x02)
++ Field (SPIM, ByteAcc, NoLock, Preserve) {
++ INDX, 8,
++ DATA, 8
++}
++
++/* SuperIO control registers */
++IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {
++ Offset (0x07),
++ CR07, 8, /* Logical device number */
++ Offset (0x2C),
++ CR2C, 8, /* GPIO3 multiplexed pin selection */
++ Offset (0x30),
++ CR30, 8, /* Logical device activation control register */
++ Offset (0xE0),
++ CRE0, 8, /* Wake control register */
++ Offset (0xE4),
++ CRE4, 8, /* Standby power control register */
++ Offset (0xE6),
++ CRE6, 8, /* Mouse wake event configuration register */
++ Offset (0xF1),
++ CRF1, 8, /* GPIO3 data register */
++ Offset (0xF3),
++ CRF3, 8, /* SUSLED mode register */
++ Offset (0xF6),
++ CRF6, 8, /* SMI/PME event generation control register */
++ Offset (0xF9),
++ CRF9, 8, /* ACPI PME configuration register */
++}
++
++/* Power Management I/O registers */
++OperationRegion(PIOR, SystemIO, 0x00000CD6, 0x00000002)
++ Field(PIOR, ByteAcc, NoLock, Preserve) {
++ PIOI, 0x00000008,
++ PIOD, 0x00000008,
++}
++IndexField (PIOI, PIOD, ByteAcc, NoLock, Preserve) {
++ Offset(0x00), /* MiscControl */
++ , 1,
++ T1EE, 1,
++ T2EE, 1,
++ Offset(0x01), /* MiscStatus */
++ , 1,
++ T1E, 1,
++ T2E, 1,
++ Offset(0x04), /* SmiWakeUpEventEnable3 */
++ , 7,
++ SSEN, 1,
++ Offset(0x07), /* SmiWakeUpEventStatus3 */
++ , 7,
++ CSSM, 1,
++ Offset(0x10), /* AcpiEnable */
++ , 6,
++ PWDE, 1,
++ Offset(0x1C), /* ProgramIoEnable */
++ , 3,
++ MKME, 1,
++ IO3E, 1,
++ IO2E, 1,
++ IO1E, 1,
++ IO0E, 1,
++ Offset(0x1D), /* IOMonitorStatus */
++ , 3,
++ MKMS, 1,
++ IO3S, 1,
++ IO2S, 1,
++ IO1S, 1,
++ IO0S,1,
++ Offset(0x20), /* AcpiPmEvtBlk */
++ APEB, 16,
++ Offset(0x36), /* GEvtLevelConfig */
++ , 6,
++ ELC6, 1,
++ ELC7, 1,
++ Offset(0x37), /* GPMLevelConfig0 */
++ , 3,
++ PLC0, 1,
++ PLC1, 1,
++ PLC2, 1,
++ PLC3, 1,
++ PLC8, 1,
++ Offset(0x38), /* GPMLevelConfig1 */
++ , 1,
++ PLC4, 1,
++ PLC5, 1,
++ , 1,
++ PLC6, 1,
++ PLC7, 1,
++ Offset(0x3B), /* PMEStatus1 */
++ GP0S, 1,
++ GM4S, 1,
++ GM5S, 1,
++ APS, 1,
++ GM6S, 1,
++ GM7S, 1,
++ GP2S, 1,
++ STSS, 1,
++ Offset(0x55), /* SoftPciRst */
++ SPRE, 1,
++ , 1,
++ , 1,
++ PNAT, 1,
++ PWMK, 1,
++ PWNS, 1,
++
++ /* Offset(0x61), */ /* Options_1 */
++ /* ,7, */
++ /* R617,1, */
++
++ Offset(0x65), /* UsbPMControl */
++ , 4,
++ URRE, 1,
++ , 2,
++ BCDL, 1,
++ Offset(0x68), /* MiscEnable68 */
++ , 2,
++ MAPC, 1,
++ TMTE, 1,
++ , 1,
++ Offset(0x7C), /* MiscEnable7C */
++ , 2,
++ BLNK, 2,
++ Offset(0x92), /* GEVENTIN */
++ , 7,
++ E7IS, 1,
++ Offset(0x96), /* GPM98IN */
++ G8IS, 1,
++ G9IS, 1,
++ Offset(0x9A), /* EnhanceControl */
++ ,7,
++ HPDE, 1,
++ Offset(0xA8), /* PIO7654Enable */
++ IO4E, 1,
++ IO5E, 1,
++ IO6E, 1,
++ IO7E, 1,
++ Offset(0xA9), /* PIO7654Status */
++ IO4S, 1,
++ IO5S, 1,
++ IO6S, 1,
++ IO7S, 1,
++}
++
++/* PM1 Event Block
++ * First word is PM1_Status, Second word is PM1_Enable
++ */
++OperationRegion(P1EB, SystemIO, APEB, 0x04)
++ Field(P1EB, ByteAcc, NoLock, Preserve) {
++ TMST, 1,
++ , 3,
++ BMST, 1,
++ GBST, 1,
++ Offset(0x01),
++ PBST, 1,
++ , 1,
++ RTST, 1,
++ , 3,
++ PWST, 1,
++ SPWS, 1,
++ Offset(0x02),
++ TMEN, 1,
++ , 4,
++ GBEN, 1,
++ Offset(0x03),
++ PBEN, 1,
++ , 1,
++ RTEN, 1,
++ , 3,
++ PWDA, 1,
++}
++
++/* Wake status package */
++Name(WKST,Package() {Zero, Zero})
++
+ /*
+- * WARNING: Sleep/Wake is a work in progress and is still somewhat flaky!
++ * \_WAK System Wake method
++ *
++ * Entry:
++ * Arg0=The value of the sleeping state S1=1, S2=2
++ *
++ * Exit:
++ * Return package of 2 DWords
++ * Dword 1 - Status
++ * 0x00000000 wake succeeded
++ * 0x00000001 Wake was signaled but failed due to lack of power
++ * 0x00000002 Wake was signaled but failed due to thermal condition
++ * Dword 2 - Power Supply state
++ * if non-zero the effective S-state the power supply entered
+ */
+-
+- /* Port 80 POST card debug */
+- OperationRegion (DBG0, SystemIO, 0x80, One)
+- Field (DBG0, ByteAcc, NoLock, Preserve) {
+- DBG8, 8
+- }
+-
+- /* SuperIO control port */
+- Name (SPIO, 0x2E)
+-
+- /* SuperIO control map */
+- OperationRegion (SPIM, SystemIO, SPIO, 0x02)
+- Field (SPIM, ByteAcc, NoLock, Preserve) {
+- INDX, 8,
+- DATA, 8
+- }
+-
+- /* SuperIO control registers */
+- IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) {
+- Offset (0x07),
+- CR07, 8, /* Logical device number */
+- Offset (0x2C),
+- CR2C, 8, /* GPIO3 multiplexed pin selection */
+- Offset (0x30),
+- CR30, 8, /* Logical device activation control register */
+- Offset (0xE0),
+- CRE0, 8, /* Wake control register */
+- Offset (0xE6),
+- CRE6, 8, /* Mouse wake event configuration register */
+- Offset (0xF1),
+- CRF1, 8, /* GPIO3 data register */
+- Offset (0xF3),
+- CRF3, 8, /* SUSLED mode register */
+- Offset (0xF6),
+- CRF6, 8, /* SMI/PME event generation control register */
+- Offset (0xF9),
+- CRF9, 8, /* ACPI PME configuration register */
++Method(\_WAK, 1) {
++ Store (0x20, DBG8)
++
++ /* Set up LEDs */
++ /* Set power LED to steady on */
++ Store(0x3, BLNK)
++
++ /* Configure SuperIO for wake */
++ /* Access SuperIO ACPI device */
++ Store(0x87, INDX)
++ Store(0x87, INDX)
++ Store(0x0A, CR07)
++
++ if (LEqual(Arg0, One)) /* Resuming from power state S1 */
++ {
++ /* Deactivate the ACPI device */
++ Store(Zero, CR30)
++
++ /* Disable PS/2 SMI/PME events */
++ And(CRF6, 0xCF, CRF6)
+ }
+-
+- /* Power Management I/O registers */
+- OperationRegion(PIOR, SystemIO, 0x00000CD6, 0x00000002)
+- Field(PIOR, ByteAcc, NoLock, Preserve) {
+- PIOI, 0x00000008,
+- PIOD, 0x00000008,
+- }
+- IndexField (PIOI, PIOD, ByteAcc, NoLock, Preserve) {
+- Offset(0x00), /* MiscControl */
+- , 1,
+- T1EE, 1,
+- T2EE, 1,
+- Offset(0x01), /* MiscStatus */
+- , 1,
+- T1E, 1,
+- T2E, 1,
+- Offset(0x04), /* SmiWakeUpEventEnable3 */
+- , 7,
+- SSEN, 1,
+- Offset(0x07), /* SmiWakeUpEventStatus3 */
+- , 7,
+- CSSM, 1,
+- Offset(0x10), /* AcpiEnable */
+- , 6,
+- PWDE, 1,
+- Offset(0x1C), /* ProgramIoEnable */
+- , 3,
+- MKME, 1,
+- IO3E, 1,
+- IO2E, 1,
+- IO1E, 1,
+- IO0E, 1,
+- Offset(0x1D), /* IOMonitorStatus */
+- , 3,
+- MKMS, 1,
+- IO3S, 1,
+- IO2S, 1,
+- IO1S, 1,
+- IO0S,1,
+- Offset(0x20), /* AcpiPmEvtBlk */
+- APEB, 16,
+- Offset(0x36), /* GEvtLevelConfig */
+- , 6,
+- ELC6, 1,
+- ELC7, 1,
+- Offset(0x37), /* GPMLevelConfig0 */
+- , 3,
+- PLC0, 1,
+- PLC1, 1,
+- PLC2, 1,
+- PLC3, 1,
+- PLC8, 1,
+- Offset(0x38), /* GPMLevelConfig1 */
+- , 1,
+- PLC4, 1,
+- PLC5, 1,
+- , 1,
+- PLC6, 1,
+- PLC7, 1,
+- Offset(0x3B), /* PMEStatus1 */
+- GP0S, 1,
+- GM4S, 1,
+- GM5S, 1,
+- APS, 1,
+- GM6S, 1,
+- GM7S, 1,
+- GP2S, 1,
+- STSS, 1,
+- Offset(0x55), /* SoftPciRst */
+- SPRE, 1,
+- , 1,
+- , 1,
+- PNAT, 1,
+- PWMK, 1,
+- PWNS, 1,
+-
+- /* Offset(0x61), */ /* Options_1 */
+- /* ,7, */
+- /* R617,1, */
+-
+- Offset(0x65), /* UsbPMControl */
+- , 4,
+- URRE, 1,
+- Offset(0x68), /* MiscEnable68 */
+- , 3,
+- TMTE, 1,
+- , 1,
+- Offset(0x7C), /* MiscEnable7C */
+- , 2,
+- BLNK, 2,
+- Offset(0x92), /* GEVENTIN */
+- , 7,
+- E7IS, 1,
+- Offset(0x96), /* GPM98IN */
+- G8IS, 1,
+- G9IS, 1,
+- Offset(0x9A), /* EnhanceControl */
+- ,7,
+- HPDE, 1,
+- Offset(0xA8), /* PIO7654Enable */
+- IO4E, 1,
+- IO5E, 1,
+- IO6E, 1,
+- IO7E, 1,
+- Offset(0xA9), /* PIO7654Status */
+- IO4S, 1,
+- IO5S, 1,
+- IO6S, 1,
+- IO7S, 1,
++ if (Lor(LEqual(Arg0, 0x03), LEqual(Arg0, 0x04))) /* Resuming from power state S3 or S4 */
++ {
++ /* Disable PS/2 wake */
++ And(CRE0, 0x1D, CRE0)
++ And(CRE6, 0x7F, CRE6)
+ }
+
+- /* PM1 Event Block
+- * First word is PM1_Status, Second word is PM1_Enable
+- */
+- OperationRegion(P1EB, SystemIO, APEB, 0x04)
+- Field(P1EB, ByteAcc, NoLock, Preserve) {
+- TMST, 1,
+- , 3,
+- BMST, 1,
+- GBST, 1,
+- Offset(0x01),
+- PBST, 1,
+- , 1,
+- RTST, 1,
+- , 3,
+- PWST, 1,
+- SPWS, 1,
+- Offset(0x02),
+- TMEN, 1,
+- , 4,
+- GBEN, 1,
+- Offset(0x03),
+- PBEN, 1,
+- , 1,
+- RTEN, 1,
+- , 3,
+- PWDA, 1,
+- }
+-
+- /* Wake status package */
+- Name(WKST,Package(){Zero, Zero})
+-
+- /*
+- * \_WAK System Wake method
+- *
+- * Entry:
+- * Arg0=The value of the sleeping state S1=1, S2=2
+- *
+- * Exit:
+- * Return package of 2 DWords
+- * Dword 1 - Status
+- * 0x00000000 wake succeeded
+- * 0x00000001 Wake was signaled but failed due to lack of power
+- * 0x00000002 Wake was signaled but failed due to thermal condition
+- * Dword 2 - Power Supply state
+- * if non-zero the effective S-state the power supply entered
+- */
+- Method(\_WAK, 1) {
+- Store (0x20, DBG8)
+-
+- /* Set up LEDs */
+- /* Set power LED to steady on */
+- Store(0x3, BLNK)
+-
+- /* Configure SuperIO for wake */
+- /* Access SuperIO ACPI device */
+- Store(0x87, INDX)
+- Store(0x87, INDX)
+- Store(0x0A, CR07)
+-
+- if (LEqual(Arg0, One)) /* Resuming from power state S1 */
+- {
+- /* Deactivate the ACPI device */
+- Store(Zero, CR30)
+-
+- /* Disable PS/2 SMI/PME events */
+- And(CRF6, 0xCF, CRF6)
+- }
+- if (Lor(LEqual(Arg0, 0x03), LEqual(Arg0, 0x04))) /* Resuming from power state S3 or S4 */
+- {
+- /* Disable PS/2 wake */
+- And(CRE0, 0x1D, CRE0)
+- And(CRE6, 0x7F, CRE6)
+- }
++ /* Restore default SuperIO access */
++ Store(0xAA, INDX)
+
+- /* Restore default SuperIO access */
+- Store(0xAA, INDX)
++ Store (0x21, DBG8)
+
+- Store (0x21, DBG8)
++ /* Re-enable HPET */
++ Store(1, HPDE)
+
+- /* Re-enable HPET */
+- Store(1, HPDE)
++ /* Restore PCIRST# so it resets USB */
++ if (LEqual(Arg0, 3)){
++ Store(1, URRE)
++ }
+
+- /* Restore PCIRST# so it resets USB */
+- if (LEqual(Arg0, 3)){
+- Store(1, URRE)
+- }
++ /* Configure southbridge for wake */
++ /* Arbitrarily clear PciExpWakeStatus */
++ Store(PWST, PWST)
+
+- /* Configure southbridge for wake */
+- /* Arbitrarily clear PciExpWakeStatus */
+- Store(PWST, PWST)
++ Store (0x22, DBG8)
+
+- Store (0x22, DBG8)
++ Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
+
+- Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */
++ Return(WKST)
++}
+
+- Return(WKST)
++/*
++ * \_PTS - Prepare to Sleep method
++ *
++ * Entry:
++ * Arg0=The value of the sleeping state S1=1, S2=2, etc
++ *
++ * Exit:
++ * -none-
++ *
++ * The _PTS control method is executed at the beginning of the sleep process
++ * for S1-S5. The sleeping value is passed to the _PTS control method. This
++ * control method may be executed a relatively long time before entering the
++ * sleep state and the OS may abort the operation without notification to
++ * the ACPI driver. This method cannot modify the configuration or power
++ * state of any device in the system.
++ */
++Method(\_PTS, 1) {
++ Store (Arg0, DBG8)
++
++ /* Set up LEDs */
++ if (LEqual(Arg0, One)) /* Power state S1 requested */
++ {
++ /* Set suspend LED to 0.25Hz toggle pulse with 50% duty cycle */
++ Store(0x2, BLNK)
++ }
++ if (LEqual(Arg0, 0x3)) /* Power state S3 requested */
++ {
++ /* Set suspend LED to 0.25Hz toggle pulse with 25% duty cycle */
++ Store(0x1, BLNK)
+ }
+
+- /*
+- * \_PTS - Prepare to Sleep method
+- *
+- * Entry:
+- * Arg0=The value of the sleeping state S1=1, S2=2, etc
+- *
+- * Exit:
+- * -none-
+- *
+- * The _PTS control method is executed at the beginning of the sleep process
+- * for S1-S5. The sleeping value is passed to the _PTS control method. This
+- * control method may be executed a relatively long time before entering the
+- * sleep state and the OS may abort the operation without notification to
+- * the ACPI driver. This method cannot modify the configuration or power
+- * state of any device in the system.
+- */
+- Method(\_PTS, 1) {
+- Store (Arg0, DBG8)
+-
+- /* Set up LEDs */
+- if (LEqual(Arg0, One)) /* Power state S1 requested */
+- {
+- /* Set suspend LED to 0.25Hz toggle pulse with 50% duty cycle */
+- Store(0x2, BLNK)
+- }
+- if (LEqual(Arg0, 0x3)) /* Power state S3 requested */
+- {
+- /* Set suspend LED to 0.25Hz toggle pulse with 25% duty cycle */
+- Store(0x1, BLNK)
+- }
++ /* Configure SuperIO for sleep */
++ /* Access SuperIO ACPI device */
++ Store(0x87, INDX)
++ Store(0x87, INDX)
++ Store(0x0A, CR07)
+
+- /* Configure SuperIO for sleep */
+- /* Access SuperIO ACPI device */
+- Store(0x87, INDX)
+- Store(0x87, INDX)
+- Store(0x0A, CR07)
++ /* Disable PS/2 wakeup and connect PANSW_IN to PANSW_OUT */
++ And(CRE0, 0x1F, CRE0)
+
+- /* Disable PS/2 wakeup and connect PANSW_IN to PANSW_OUT */
+- And(CRE0, 0x1F, CRE0)
++ if (LEqual(Arg0, One)) /* Power state S1 requested */
++ {
++ /* Activate the ACPI device */
++ Store(One, CR30)
+
+- if (LEqual(Arg0, One)) /* Power state S1 requested */
+- {
+- /* Activate the ACPI device */
+- Store(One, CR30)
++ /* Disable SMI/PME events for:
++ * LPT
++ * FDC
++ * UART
++ */
++ Store(0x00, CRF6)
+
+- /* Disable SMI/PME events for:
+- * LPT
+- * FDC
+- * UART
+- Store(0x00, CRF6)
++ /* Enable PS/2 keyboard SMI/PME events */
++ Or(CRF6, 0x10, CRF6)
+
+- /* Enable PS/2 keyboard SMI/PME events */
+- Or(CRF6, 0x10, CRF6)
++ /* Enable PS/2 keyboard wake */
++ Or(CRE0, 0x40, CRE0)
+
+- /* Enable PS/2 keyboard wake */
+- Or(CRE0, 0x40, CRE0)
++ /* Enable PS/2 mouse SMI/PME events */
++ Or(CRF6, 0x20, CRF6)
+
+- /* Enable PS/2 mouse SMI/PME events */
+- Or(CRF6, 0x20, CRF6)
++ /* Enable PS/2 mouse wake */
++ Or(CRE0, 0x20, CRE0)
++ } else {
++ /* Enable PS/2 keyboard wake on any keypress */
++ Or(CRE0, 0x41, CRE0)
+
+- /* Enable PS/2 mouse wake */
+- Or(CRE0, 0x20, CRE0)
+- } else {
+- /* Enable PS/2 keyboard wake on any keypress */
+- Or(CRE0, 0x41, CRE0)
++ /* Enable PS/2 mouse wake on any click */
++ Or(CRE0, 0x22, CRE0)
++ Or(CRE6, 0x80, CRE6)
+
+- /* Enable PS/2 mouse wake on any click */
+- Or(CRE0, 0x22, CRE0)
+- Or(CRE6, 0x80, CRE6)
++ if (LEqual(Arg0, 0x03)) /* Power state S3 requested */
++ {
++ /* Set VSBGATE# to provide standby power during S3 */
++ Or(CRE4, 0x10, CRE4)
+ }
++ }
+
+- /* Restore default SuperIO access */
+- Store(0xAA, INDX)
++ /* Restore default SuperIO access */
++ Store(0xAA, INDX)
+
+- Store (0x10, DBG8)
++ Store (0x10, DBG8)
+
+- /* Don't allow PCIRST# to reset USB */
+- if (LEqual(Arg0, 3)){
+- Store(0, URRE)
+- }
++ /* Don't allow PCIRST# to reset USB */
++ if (LEqual(Arg0, 3)){
++ Store(0, URRE)
++ }
+
+- /* Configure southbridge for sleep */
+- /* Clear sleep SMI status flag and enable sleep SMI trap. */
+- // Store(One, CSSM) /* Set ExtEvent0 as SMI# source */
+- // Store(One, SSEN) /* Enable wake on external event 0 */
++ /* Configure southbridge for sleep */
++ /* Use bus clock for delay timebase */
++ Store(0, BCDL)
++ /* Defer APIC interrupts until first ACPI access */
++ Store(One, MAPC)
+
+- /* On older chips, clear PciExpWakeDisEn */
+- // if (LLessEqual(SBRI, 0x13)) {
+- // Store(0, PWDE)
+- // }
++ /* On older chips, clear PciExpWakeDisEn */
++ // if (LLessEqual(SBRI, 0x13)) {
++ // Store(0, PWDE)
++ // }
+
+- Store (0x11, DBG8)
++ Store (0x11, DBG8)
+
+- /* Clear wake status structure. */
+- Store(0, Index(WKST,0))
+- Store(0, Index(WKST,1))
+- }
+\ No newline at end of file
++ /* Clear wake status structure. */
++ Store(0, Index(WKST,0))
++ Store(0, Index(WKST,1))
++}
+diff --git a/src/mainboard/asus/kgpe-d16/dsdt.asl b/src/mainboard/asus/kgpe-d16/dsdt.asl
+index bdd1d2d..b6f10d9 100644
+--- a/src/mainboard/asus/kgpe-d16/dsdt.asl
++++ b/src/mainboard/asus/kgpe-d16/dsdt.asl
+@@ -58,9 +58,8 @@ DefinitionBlock (
+ /* Define power states */
+ Name (\_S0, Package () { 0x00, 0x00, 0x00, 0x00 }) /* Normal operation */
+ Name (\_S1, Package () { 0x01, 0x01, 0x00, 0x00 }) /* Standby */
+- Name (\_S2, Package () { 0x02, 0x02, 0x00, 0x00 }) /* Standby w/ CPU shutdown */
+- Name (\_S3, Package () { 0x03, 0x00, 0x00, 0x00 }) /* Suspend */
+- /* Name (\_S4, Package () { 0x04, 0x04, 0x00, 0x00 }) */
++ Name (\_S3, Package () { 0x03, 0x03, 0x00, 0x00 }) /* Suspend to RAM */
++ Name (\_S4, Package () { 0x04, 0x04, 0x00, 0x00 }) /* Suspend to disk */
+ Name (\_S5, Package () { 0x05, 0x05, 0x00, 0x00 }) /* Hard power off */
+
+ /* The _PIC method is called by the OS to choose between interrupt
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 616fdfb..3431bab 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -224,12 +224,15 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
+ struct sys_info *sysinfo = &sysinfo_car;
+
+- u32 bsp_apicid = 0, val;
++ uint32_t bsp_apicid = 0, val;
++ uint8_t byte;
+ msr_t msr;
+
+ timestamp_init(timestamp_get());
+ timestamp_add_now(TS_START_ROMSTAGE);
+
++ int s3resume = acpi_is_wakeup_s3();
++
+ if (!cpu_init_detectedx && boot_cpu()) {
+ /* Nothing special needs to be done to find bus 0 */
+ /* Allow the HT devices to be found */
+@@ -247,6 +250,11 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ /* Initialize early serial */
+ nuvoton_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
+ console_init();
++
++ /* Disable LPC legacy DMA support to prevent lockup */
++ byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0x78);
++ byte &= ~(1 << 0);
++ pci_write_config8(PCI_DEV(0, 0x14, 3), 0x78, byte);
+ }
+
+ post_code(0x30);
+@@ -285,14 +293,6 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ amd_ht_fixup(sysinfo);
+ post_code(0x35);
+
+- /* Set DDR memory voltage
+- * FIXME
+- * This should be set based on the output of the DIMM SPDs
+- * For now it is locked to 1.5V
+- */
+- set_ddr3_voltage(0, 0); /* Node 0 */
+- set_ddr3_voltage(1, 0); /* Node 1 */
+-
+ /* Setup nodes PCI space and start core 0 AP init. */
+ finalize_node_setup(sysinfo);
+
+@@ -355,6 +355,17 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ die("After soft_reset_x - shouldn't see this message!!!\n");
+ }
+
++ /* Set DDR memory voltage
++ * FIXME
++ * This should be set based on the output of the DIMM SPDs
++ * For now it is locked to 1.5V
++ */
++ set_lpc_sticky_ctl(1); /* Retain LPC/IMC GPIO configuration during S3 sleep */
++ if (!s3resume) { /* Avoid supply voltage glitches while the DIMMs are retaining data */
++ set_ddr3_voltage(0, 0); /* Node 0 */
++ set_ddr3_voltage(1, 0); /* Node 1 */
++ }
++
+ /* Set up peripheral control lines */
+ set_peripheral_control_lines();
+
+@@ -384,7 +395,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ timestamp_add_now(TS_AFTER_INITRAM);
+
+ #if !IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
+- cbmem_initialize_empty();
++ if (s3resume)
++ cbmem_initialize();
++ else
++ cbmem_initialize_empty();
+ post_code(0x41);
+
+ amdmct_cbmem_store_info(sysinfo);
+diff --git a/src/mainboard/asus/kgpe-d16/spd_notes.txt b/src/mainboard/asus/kgpe-d16/spd_notes.txt
+index 623a88f..ddd5cc8 100644
+--- a/src/mainboard/asus/kgpe-d16/spd_notes.txt
++++ b/src/mainboard/asus/kgpe-d16/spd_notes.txt
+@@ -28,3 +28,19 @@ Other hardware
+
+ RECOVERY1 middle pin is connected to southbridge (AMD SP5100) GPIO 61
+ Normal is HIGH, recovery is LOW.
++
+++12VSB is generated using a charge pump attached to pin 7 of PU24 (APW7145).
++
++The +12VSB standby voltage to each bank of DIMMs is switched by a bank of small FETs located close to each RAM power regulator control chip.
++The +12V primary voltage (lower left pin of the FET placed on the upper left of the control chip of the second node) is also connected to the 232GE located near the PCI slot.
++
++The control line running to the gates of the +12VSB control FETs is connected to the +5VSB power for the USB ports.
++That line in turn is connected to +5VSB via the lone P06P03G PMOS transistor on the reverse side of the board, near the center on the lower half.
++The gate of that transistor is connected via a resistor to the source of the P06P03G PMOS transistor located adjacent to the unpopulated SMA clock header.
++The gate of that transistor is connected directly to the drain of the small FET directly below it.
++After that, there's a cascade of small FETs and resistors in that region, eventually leading to SuperIO pin 81.
++
++SuperIO pin 81 (VSBGATE#) enables the standby voltage rails when set LOW.
++VSBGATE# is reset on every assertion of PWRGOOD.
++
++Setting SuperIO LDN 9 CRF4 bits 1 or 0 (or both) to 0 disables NICB.
+\ No newline at end of file
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0025-include-smbios-Update-SMBIOS-memory-structures-to-ve.patch b/resources/libreboot/patch/kgpe-d16/0025-include-smbios-Update-SMBIOS-memory-structures-to-ve.patch
new file mode 100644
index 00000000..e4ec5430
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0025-include-smbios-Update-SMBIOS-memory-structures-to-ve.patch
@@ -0,0 +1,31 @@
+From 10204a5acccb903d407b241501e316506199f60d Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:00:34 -0500
+Subject: [PATCH 025/146] include/smbios: Update SMBIOS memory structures to
+ version 2.8
+
+---
+ src/include/smbios.h | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/include/smbios.h b/src/include/smbios.h
+index b654c23..fdb7bbd 100644
+--- a/src/include/smbios.h
++++ b/src/include/smbios.h
+@@ -409,9 +409,11 @@ struct smbios_type17 {
+ u8 asset_tag;
+ u8 part_number;
+ u8 attributes;
+- u16 extended_size;
++ u32 extended_size;
+ u16 clock_speed;
+-
++ u16 minimum_voltage;
++ u16 maximum_voltage;
++ u16 configured_voltage;
+ char eos[2];
+ } __attribute__((packed));
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0026-mainboard-asus-kgpe-d16-Set-DDR3-memory-voltage-base.patch b/resources/libreboot/patch/kgpe-d16/0026-mainboard-asus-kgpe-d16-Set-DDR3-memory-voltage-base.patch
new file mode 100644
index 00000000..bdf17ce1
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0026-mainboard-asus-kgpe-d16-Set-DDR3-memory-voltage-base.patch
@@ -0,0 +1,151 @@
+From b5d34134e15285aeff92567703530d5c7d372562 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 10 May 2015 04:37:56 -0500
+Subject: [PATCH 026/146] mainboard/asus/kgpe-d16: Set DDR3 memory voltage
+ based on SPD data
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 5 ++
+ src/mainboard/asus/kgpe-d16/romstage.c | 76 ++++++++++++++++++++++++++++--
+ 3 files changed, 77 insertions(+), 5 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index e920297..39a4778 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -6,6 +6,7 @@ iommu = Disable
+ nmi = Disable
+ hypertransport_speed_limit = Auto
+ max_mem_clock = DDR3-1600
++minimum_memory_voltage = 1.5V
+ ECC_memory = Enable
+ ECC_redirection = Disable
+ ecc_scrub_rate = 1.28us
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index b9266dc..110e0bb 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -41,6 +41,7 @@ entries
+ 456 1 e 1 ECC_memory
+ 457 1 e 1 ECC_redirection
+ 458 4 e 11 hypertransport_speed_limit
++462 2 e 12 minimum_memory_voltage
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+@@ -129,6 +130,10 @@ enumerations
+ 11 13 400MHz
+ 11 14 300MHz
+ 11 15 200MHz
++12 0 1.5V
++12 1 1.35V
++12 2 1.25V
++12 3 1.15V
+
+ checksums
+
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 3431bab..18e7c16 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -135,7 +135,7 @@ static void activate_spd_rom(const struct mem_controller *ctrl) {
+ */
+ static void set_ddr3_voltage(uint8_t node, uint8_t index) {
+ uint8_t byte;
+- uint8_t value;
++ uint8_t value = 0;
+
+ if (index == 0)
+ value = 0x0;
+@@ -161,6 +161,74 @@ static void set_ddr3_voltage(uint8_t node, uint8_t index) {
+ byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0xd0);
+ byte &= 0x0f;
+ pci_write_config8(PCI_DEV(0, 0x14, 3), 0xd0, byte);
++
++ printk(BIOS_DEBUG, "Node %02d DIMM voltage set to index %02x\n", node, index);
++}
++
++void DIMMSetVoltages(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstatA) {
++ /* This mainboard allows the DIMM voltage to be set per-socket.
++ * Therefore, for each socket, iterate over all DIMMs to find the
++ * lowest supported voltage common to all DIMMs on that socket.
++ */
++ uint8_t nvram;
++ uint8_t dimm;
++ uint8_t node;
++ uint8_t socket;
++ uint8_t allowed_voltages = 0xf; /* The mainboard VRMs allow 1.15V, 1.25V, 1.35V, and 1.5V */
++ uint32_t set_voltage = 0;
++
++ if (get_option(&nvram, "minimum_memory_voltage") == CB_SUCCESS) {
++ if (nvram == 2)
++ allowed_voltages = 0x7; /* Allow 1.25V, 1.35V, and 1.5V */
++ if (nvram == 1)
++ allowed_voltages = 0x3; /* Allow 1.35V and 1.5V */
++ if (nvram == 0)
++ allowed_voltages = 0x1; /* Allow 1.5V only */
++ }
++
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ socket = node / 2;
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + node;
++ if (pDCTstat->NodePresent) {
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ if (pDCTstat->DIMMValid & (1 << dimm)) {
++ allowed_voltages &= pDCTstat->DimmSupportedVoltages[dimm];
++ }
++ }
++ }
++
++ if (pDCTstat->NodePresent && (node & 0x1)) {
++ /* Set voltages */
++ if (allowed_voltages & 0x8) {
++ set_voltage = 1150;
++ set_ddr3_voltage(socket, 3);
++ } else if (allowed_voltages & 0x4) {
++ set_voltage = 1250;
++ set_ddr3_voltage(socket, 2);
++ } else if (allowed_voltages & 0x2) {
++ set_voltage = 1350;
++ set_ddr3_voltage(socket, 1);
++ } else {
++ set_voltage = 1500;
++ set_ddr3_voltage(socket, 0);
++ }
++
++ /* Save final DIMM voltages for SMBIOS use */
++ if (pDCTstat->NodePresent) {
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ pDCTstat->DimmConfiguredVoltage[dimm] = set_voltage;
++ }
++ }
++ pDCTstat = pDCTstatA + (node - 1);
++ if (pDCTstat->NodePresent) {
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ pDCTstat->DimmConfiguredVoltage[dimm] = set_voltage;
++ }
++ }
++ }
++ }
+ }
+
+ static void set_peripheral_control_lines(void) {
+@@ -355,10 +423,8 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ die("After soft_reset_x - shouldn't see this message!!!\n");
+ }
+
+- /* Set DDR memory voltage
+- * FIXME
+- * This should be set based on the output of the DIMM SPDs
+- * For now it is locked to 1.5V
++ /* Set default DDR memory voltage
++ * This will be overridden later during RAM initialization
+ */
+ set_lpc_sticky_ctl(1); /* Retain LPC/IMC GPIO configuration during S3 sleep */
+ if (!s3resume) { /* Avoid supply voltage glitches while the DIMMs are retaining data */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0027-northbridge-amd-amdfam10-Set-DIMM-voltage-based-on-S.patch b/resources/libreboot/patch/kgpe-d16/0027-northbridge-amd-amdfam10-Set-DIMM-voltage-based-on-S.patch
new file mode 100644
index 00000000..7c45877d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0027-northbridge-amd-amdfam10-Set-DIMM-voltage-based-on-S.patch
@@ -0,0 +1,176 @@
+From ca270728b0d2232efb4384f645d993403f4bc24e Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 18:56:05 -0500
+Subject: [PATCH 027/146] northbridge/amd/amdfam10: Set DIMM voltage based on
+ SPD data
+
+---
+ src/northbridge/amd/amdfam10/acpi.c | 3 +-
+ src/northbridge/amd/amdfam10/northbridge.c | 73 ++++++++++++++++++++++-----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 6 +++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 6 +++
+ 4 files changed, 73 insertions(+), 15 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/acpi.c b/src/northbridge/amd/amdfam10/acpi.c
+index 4b86e96..92433bb 100644
+--- a/src/northbridge/amd/amdfam10/acpi.c
++++ b/src/northbridge/amd/amdfam10/acpi.c
+@@ -307,8 +307,7 @@ void northbridge_acpi_write_vars(device_t device)
+ } else {
+ if((sysconf.pci1234[0] >> 12) & 0xff) { //sb chain on other than bus 0
+ CBST = (u8) (0x0f);
+- }
+- else {
++ } else {
+ CBST = (u8) (0x00);
+ }
+ }
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 880129b..d0a0787 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -948,19 +948,38 @@ static int amdfam10_get_smbios_data16(int* count, int handle, unsigned long *cur
+
+ static uint16_t amdmct_mct_speed_enum_to_mhz(uint8_t speed)
+ {
+- switch (speed) {
+- case 1:
+- return 200;
+- case 2:
+- return 266;
+- case 3:
+- return 333;
+- case 4:
+- return 400;
+- case 5:
+- return 533;
+- default:
+- return 0;
++ if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
++ switch (speed) {
++ case 1:
++ return 200;
++ case 2:
++ return 266;
++ case 3:
++ return 333;
++ case 4:
++ return 400;
++ case 5:
++ return 533;
++ default:
++ return 0;
++ }
++ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ switch (speed) {
++ case 3:
++ return 333;
++ case 4:
++ return 400;
++ case 5:
++ return 533;
++ case 6:
++ return 667;
++ case 7:
++ return 800;
++ default:
++ return 0;
++ }
++ } else {
++ return 0;
+ }
+ }
+
+@@ -1046,6 +1065,34 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ snprintf(string_buffer, sizeof (string_buffer), "%08X", mem_info->dct_stat[node].DimmSerialNumber[slot]);
+ t->serial_number = smbios_add_string(t->eos, string_buffer);
+ }
++ if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
++ /* JEDEC specifies 1.8V only, so assume that the memory is configured for 1.8V */
++ t->minimum_voltage = 1800;
++ t->maximum_voltage = 1800;
++ t->configured_voltage = 1800;
++ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ /* Find the maximum and minimum supported voltages */
++ uint8_t supported_voltages = mem_info->dct_stat[node].DimmSupportedVoltages[slot];
++ if (supported_voltages & 0x8)
++ t->minimum_voltage = 1150;
++ else if (supported_voltages & 0x4)
++ t->minimum_voltage = 1250;
++ else if (supported_voltages & 0x2)
++ t->minimum_voltage = 1350;
++ else if (supported_voltages & 0x1)
++ t->minimum_voltage = 1500;
++
++ if (supported_voltages & 0x1)
++ t->maximum_voltage = 1500;
++ else if (supported_voltages & 0x2)
++ t->maximum_voltage = 1350;
++ else if (supported_voltages & 0x4)
++ t->maximum_voltage = 1250;
++ else if (supported_voltages & 0x8)
++ t->maximum_voltage = 1150;
++
++ t->configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot];
++ }
+ t->memory_error_information_handle = 0xFFFE; /* no error information handle available */
+ single_len = t->length + smbios_string_table_len(t->eos);
+ len += single_len;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 0c06444..303c6c7 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -327,6 +327,9 @@ restartinit:
+ printk(BIOS_DEBUG, "No Nodes?!\n");
+ goto fatalexit;
+ }
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
++ DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+@@ -2120,6 +2123,9 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DimmBanks[i] = 1ULL << (((mctRead_SPD(smbaddr, SPD_Density) & 0x70) >> 4) + 3);
+ pDCTstat->DimmWidth[i] = 1ULL << ((mctRead_SPD(smbaddr, SPD_BusWidth) & 0x7) + 3);
+ }
++ /* Check supported voltage(s) */
++ pDCTstat->DimmSupportedVoltages[i] = mctRead_SPD(smbaddr, SPD_Voltage) & 0x7;
++ pDCTstat->DimmSupportedVoltages[i] ^= 0x1; /* Invert LSB to convert from SPD format to internal bitmap format */
+ /* Check module type */
+ byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE) & 0x7;
+ if (byte == JED_RDIMM || byte == JED_MiniRDIMM) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index c790d7e..a947c2d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -206,6 +206,7 @@
+ #define JED_MiniRDIMM 0x5 /* Mini-RDIMM */
+ #define SPD_Density 4 /* Bank address bits,SDRAM capacity */
+ #define SPD_Addressing 5 /* Row/Column address bits */
++#define SPD_Voltage 6 /* Supported voltage bitfield */
+ #define SPD_Organization 7 /* rank#,Device width */
+ #define SPD_BusWidth 8 /* ECC, Bus width */
+ #define JED_ECC 8 /* ECC capability */
+@@ -585,6 +586,10 @@ struct DCTStatStruc { /* A per Node structure*/
+ struct _sDCTStruct s_C_DCTPtr[2];
+ /* struct _sDCTStruct s_C_DCT1Ptr[8]; */
+
++ /* DIMM supported voltage bitmap ([2:0]: 1.25V, 1.35V, 1.5V) */
++ uint8_t DimmSupportedVoltages[MAX_DIMMS_SUPPORTED];
++ uint32_t DimmConfiguredVoltage[MAX_DIMMS_SUPPORTED]; /* mV */
++
+ uint8_t DimmRows[MAX_DIMMS_SUPPORTED];
+ uint8_t DimmCols[MAX_DIMMS_SUPPORTED];
+ uint8_t DimmRanks[MAX_DIMMS_SUPPORTED];
+@@ -905,6 +910,7 @@ u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val);
+ void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct);
+ void mctGet_DIMMAddr(struct DCTStatStruc *pDCTstat, u32 node);
+ void mctSMBhub_Init(u32 node);
++void DIMMSetVoltages(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ int mctRead_SPD(u32 smaddr, u32 reg);
+ void InterleaveNodes_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void InterleaveChannels_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0028-src-console-Add-x86-romstage-spinlock-option.patch b/resources/libreboot/patch/kgpe-d16/0028-src-console-Add-x86-romstage-spinlock-option.patch
new file mode 100644
index 00000000..97b6fa62
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0028-src-console-Add-x86-romstage-spinlock-option.patch
@@ -0,0 +1,98 @@
+From 14dead11fc372b694a24358216b60bdd274d8d21 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 18 May 2015 16:04:10 -0500
+Subject: [PATCH 028/146] src/console: Add x86 romstage spinlock option
+
+---
+ src/arch/x86/include/arch/smp/spinlock.h | 10 +++++++++-
+ src/console/printk.c | 19 +++++++++++++++++++
+ 2 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/src/arch/x86/include/arch/smp/spinlock.h b/src/arch/x86/include/arch/smp/spinlock.h
+index 32be2f2..a5904c7 100644
+--- a/src/arch/x86/include/arch/smp/spinlock.h
++++ b/src/arch/x86/include/arch/smp/spinlock.h
+@@ -1,7 +1,7 @@
+ #ifndef ARCH_SMP_SPINLOCK_H
+ #define ARCH_SMP_SPINLOCK_H
+
+-#ifndef __PRE_RAM__
++#if !defined(__PRE_RAM__) || defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
+
+ /*
+ * Your basic SMP spinlocks, allowing only a single CPU anywhere
+@@ -11,9 +11,17 @@ typedef struct {
+ volatile unsigned int lock;
+ } spinlock_t;
+
++#ifdef __PRE_RAM__
++spinlock_t* romstage_console_lock(void);
++#endif
+
+ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 }
++
++#ifndef __PRE_RAM__
+ #define DECLARE_SPIN_LOCK(x) static spinlock_t x = SPIN_LOCK_UNLOCKED;
++#else
++#define DECLARE_SPIN_LOCK(x)
++#endif
+
+ /*
+ * Simple spin lock operations. There are two variants, one clears IRQ's
+diff --git a/src/console/printk.c b/src/console/printk.c
+index aab7ff5..2aae980 100644
+--- a/src/console/printk.c
++++ b/src/console/printk.c
+@@ -2,6 +2,7 @@
+ * blatantly copied from linux/kernel/printk.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ */
+
+@@ -13,7 +14,13 @@
+ #include <stddef.h>
+ #include <trace.h>
+
++#if defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
++#ifndef __PRE_RAM__
+ DECLARE_SPIN_LOCK(console_lock)
++#endif
++#else
++DECLARE_SPIN_LOCK(console_lock)
++#endif
+
+ void do_putchar(unsigned char byte)
+ {
+@@ -39,7 +46,13 @@ int do_printk(int msg_level, const char *fmt, ...)
+ #endif
+
+ DISABLE_TRACE;
++#ifdef __PRE_RAM__
++#ifdef CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++ spin_lock(romstage_console_lock());
++#endif
++#else
+ spin_lock(&console_lock);
++#endif
+
+ va_start(args, fmt);
+ i = vtxprintf(wrap_putchar, fmt, args, NULL);
+@@ -47,7 +60,13 @@ int do_printk(int msg_level, const char *fmt, ...)
+
+ console_tx_flush();
+
++#ifdef __PRE_RAM__
++#ifdef CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++ spin_unlock(romstage_console_lock());
++#endif
++#else
+ spin_unlock(&console_lock);
++#endif
+ ENABLE_TRACE;
+
+ return i;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0029-northbridge-amd-amdmct-mct_ddr3-Fix-S3-suspend-overr.patch b/resources/libreboot/patch/kgpe-d16/0029-northbridge-amd-amdmct-mct_ddr3-Fix-S3-suspend-overr.patch
new file mode 100644
index 00000000..3c734e06
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0029-northbridge-amd-amdmct-mct_ddr3-Fix-S3-suspend-overr.patch
@@ -0,0 +1,49 @@
+From 0004a1c007784bd577e2323061cd12460b76b2a8 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 31 May 2015 18:46:40 -0500
+Subject: [PATCH 029/146] northbridge/amd/amdmct/mct_ddr3: Fix S3 suspend
+ overrunning the stack size limit
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 79576d6..2ea7dc1 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -545,10 +545,17 @@ int8_t save_mct_information_to_nvram(void)
+
+ struct spi_flash *flash;
+ ssize_t s3nv_offset;
+- struct amd_s3_persistent_data persistent_data;
++ struct amd_s3_persistent_data *persistent_data;
++
++ /* Allocate temporary data structures */
++ persistent_data = malloc(sizeof(struct amd_s3_persistent_data));
++ if (!persistent_data) {
++ printk(BIOS_DEBUG, "Could not allocate S3 data structure in RAM\n");
++ return -1;
++ }
+
+ /* Obtain MCT configuration data */
+- copy_mct_data_to_save_variable(&persistent_data);
++ copy_mct_data_to_save_variable(persistent_data);
+
+ /* Obtain CBFS file offset */
+ s3nv_offset = get_s3nv_file_offset();
+@@ -578,7 +585,10 @@ int8_t save_mct_information_to_nvram(void)
+
+ /* Erase and write data structure */
+ flash->erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
+- flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), &persistent_data);
++ flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), persistent_data);
++
++ /* Deallocate temporary data structures */
++ free(persistent_data);
+
+ /* Tear down SPI flash access */
+ flash->spi->rw = SPI_WRITE_FLAG;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0030-northbridge-amd-amdmct-mct_ddr3-Fix-failing-S3-resum.patch b/resources/libreboot/patch/kgpe-d16/0030-northbridge-amd-amdmct-mct_ddr3-Fix-failing-S3-resum.patch
new file mode 100644
index 00000000..baae3c5b
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0030-northbridge-amd-amdmct-mct_ddr3-Fix-failing-S3-resum.patch
@@ -0,0 +1,46 @@
+From e88253116622c39c99511cef99791bf0d8422e95 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 1 Jun 2015 02:40:24 -0500
+Subject: [PATCH 030/146] northbridge/amd/amdmct/mct_ddr3: Fix failing S3
+ resume
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 2ea7dc1..98b533b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -604,7 +604,9 @@ int8_t save_mct_information_to_nvram(void)
+ int8_t restore_mct_information_from_nvram(void)
+ {
+ ssize_t s3nv_offset;
+- struct amd_s3_persistent_data persistent_data;
++ struct amd_s3_persistent_data *persistent_data;
++ struct cbfs_media default_media;
++ struct cbfs_media* media = CBFS_DEFAULT_MEDIA;
+
+ /* Obtain CBFS file offset */
+ s3nv_offset = get_s3nv_file_offset();
+@@ -615,8 +617,14 @@ int8_t restore_mct_information_from_nvram(void)
+ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
+ s3nv_offset += CONFIG_S3_DATA_SIZE;
+
+- cbfs_read(CBFS_DEFAULT_MEDIA, &persistent_data, s3nv_offset, sizeof(struct amd_s3_persistent_data));
+- restore_mct_data_from_save_variable(&persistent_data);
++ /* Map data structure in CBFS and restore settings */
++ if (init_backing_media(&media, &default_media))
++ return -1;
++
++ media->open(media);
++ persistent_data = media->map(media, s3nv_offset, sizeof(struct amd_s3_persistent_data));
++ restore_mct_data_from_save_variable(persistent_data);
++ media->close(media);
+
+ return 0;
+ }
+\ No newline at end of file
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0031-northbridge-amd-amdmct-Fix-S3-suspend-resume-with-la.patch b/resources/libreboot/patch/kgpe-d16/0031-northbridge-amd-amdmct-Fix-S3-suspend-resume-with-la.patch
new file mode 100644
index 00000000..6754fc18
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0031-northbridge-amd-amdmct-Fix-S3-suspend-resume-with-la.patch
@@ -0,0 +1,78 @@
+From 67497b91b5d5e9d28264cbbcdfb772e40860ad09 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 2 Jun 2015 15:55:35 -0500
+Subject: [PATCH 031/146] northbridge/amd/amdmct: Fix S3 suspend/resume with
+ latest CBFS changes
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 28 ++++++++++++-------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 98b533b..c9bcac1 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -36,17 +36,15 @@ static ssize_t get_s3nv_file_offset(void);
+
+ ssize_t get_s3nv_file_offset(void)
+ {
+- struct cbfs_file file;
+- ssize_t offset;
+- struct cbfs_media *media = CBFS_DEFAULT_MEDIA;
+-
+- offset = cbfs_locate_file(media, &file, S3NV_FILE_NAME);
+- if (offset < 0) {
++ struct region_device s3nv_region;
++ struct cbfsf s3nv_cbfs_file;
++ if (cbfs_boot_locate(&s3nv_cbfs_file, S3NV_FILE_NAME, NULL)) {
+ printk(BIOS_DEBUG, "S3 state file not found in CBFS: %s\n", S3NV_FILE_NAME);
+ return -1;
+ }
++ cbfs_file_data(&s3nv_region, &s3nv_cbfs_file);
+
+- return offset;
++ return s3nv_region.region.offset;
+ }
+
+ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
+@@ -604,9 +602,9 @@ int8_t save_mct_information_to_nvram(void)
+ int8_t restore_mct_information_from_nvram(void)
+ {
+ ssize_t s3nv_offset;
++ ssize_t s3nv_file_offset;
++ void * s3nv_cbfs_file_ptr;
+ struct amd_s3_persistent_data *persistent_data;
+- struct cbfs_media default_media;
+- struct cbfs_media* media = CBFS_DEFAULT_MEDIA;
+
+ /* Obtain CBFS file offset */
+ s3nv_offset = get_s3nv_file_offset();
+@@ -614,17 +612,19 @@ int8_t restore_mct_information_from_nvram(void)
+ return -1;
+
+ /* Align flash pointer to nearest boundary */
++ s3nv_file_offset = s3nv_offset;
+ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
+ s3nv_offset += CONFIG_S3_DATA_SIZE;
++ s3nv_file_offset = s3nv_offset - s3nv_file_offset;
+
+ /* Map data structure in CBFS and restore settings */
+- if (init_backing_media(&media, &default_media))
++ s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL);
++ if (!s3nv_cbfs_file_ptr) {
++ printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME);
+ return -1;
+-
+- media->open(media);
+- persistent_data = media->map(media, s3nv_offset, sizeof(struct amd_s3_persistent_data));
++ }
++ persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset);
+ restore_mct_data_from_save_variable(persistent_data);
+- media->close(media);
+
+ return 0;
+ }
+\ No newline at end of file
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0032-src-console-Add-x86-printk-spinlock-support.patch b/resources/libreboot/patch/kgpe-d16/0032-src-console-Add-x86-printk-spinlock-support.patch
new file mode 100644
index 00000000..9f4c7056
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0032-src-console-Add-x86-printk-spinlock-support.patch
@@ -0,0 +1,126 @@
+From c15c778ced025a5a778e4c1d7ad03a40529f4d41 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:23:49 -0500
+Subject: [PATCH 032/146] src/console: Add x86 printk spinlock support
+
+---
+ src/Kconfig | 4 ++++
+ src/arch/x86/include/arch/smp/spinlock.h | 1 +
+ src/console/printk.c | 6 +++---
+ src/cpu/amd/car/post_cache_as_ram.c | 22 +++++++++++++++-------
+ 4 files changed, 23 insertions(+), 10 deletions(-)
+
+diff --git a/src/Kconfig b/src/Kconfig
+index bab05f2..2e6b5bc 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -447,6 +447,10 @@ config HAVE_HARD_RESET
+ This variable specifies whether a given board has a hard_reset
+ function, no matter if it's provided by board code or chipset code.
+
++config HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++ bool
++ default n
++
+ config HAVE_MONOTONIC_TIMER
+ def_bool n
+ help
+diff --git a/src/arch/x86/include/arch/smp/spinlock.h b/src/arch/x86/include/arch/smp/spinlock.h
+index a5904c7..5000779 100644
+--- a/src/arch/x86/include/arch/smp/spinlock.h
++++ b/src/arch/x86/include/arch/smp/spinlock.h
+@@ -13,6 +13,7 @@ typedef struct {
+
+ #ifdef __PRE_RAM__
+ spinlock_t* romstage_console_lock(void);
++void initialize_romstage_console_lock(void);
+ #endif
+
+ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 }
+diff --git a/src/console/printk.c b/src/console/printk.c
+index 2aae980..5a23db0 100644
+--- a/src/console/printk.c
++++ b/src/console/printk.c
+@@ -14,7 +14,7 @@
+ #include <stddef.h>
+ #include <trace.h>
+
+-#if defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
+ #ifndef __PRE_RAM__
+ DECLARE_SPIN_LOCK(console_lock)
+ #endif
+@@ -47,7 +47,7 @@ int do_printk(int msg_level, const char *fmt, ...)
+
+ DISABLE_TRACE;
+ #ifdef __PRE_RAM__
+-#ifdef CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
+ spin_lock(romstage_console_lock());
+ #endif
+ #else
+@@ -61,7 +61,7 @@ int do_printk(int msg_level, const char *fmt, ...)
+ console_tx_flush();
+
+ #ifdef __PRE_RAM__
+-#ifdef CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
+ spin_unlock(romstage_console_lock());
+ #endif
+ #else
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index e265de1..e7a41e5 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -84,6 +84,10 @@ static void prepare_ramstage_region(void *resume_backup_memory)
+ memset_((void*)0, 0, CONFIG_RAMTOP - backup_top);
+ }
+
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
++ initialize_romstage_console_lock();
++#endif
++
+ print_car_debug("Done\n");
+ }
+
+@@ -92,18 +96,19 @@ static void prepare_ramstage_region(void *resume_backup_memory)
+ static void vErrata343(void)
+ {
+ #ifdef BU_CFG2_MSR
+- msr_t msr;
+- unsigned int uiMask = 0xFFFFFFF7;
+-
+- msr = rdmsr(BU_CFG2_MSR);
+- msr.hi &= uiMask; // set bit 35 to 0
+- wrmsr(BU_CFG2_MSR, msr);
++ msr_t msr;
++ unsigned int uiMask = 0xFFFFFFF7;
++
++ msr = rdmsr(BU_CFG2_MSR);
++ msr.hi &= uiMask; // IcDisSpecTlbWr (bit 35) = 0
++ wrmsr(BU_CFG2_MSR, msr);
+ #endif
+ }
+
+ void post_cache_as_ram(void)
+ {
+ void *resume_backup_memory = NULL;
++ uint32_t family = amd_fam1x_cpu_family();
+
+ struct romstage_handoff *handoff;
+ handoff = romstage_handoff_find_or_add();
+@@ -120,7 +125,10 @@ void post_cache_as_ram(void)
+ prepare_romstage_ramstack(resume_backup_memory);
+
+ /* from here don't store more data in CAR */
+- vErrata343();
++ if (family < 0x6f) {
++ /* Family 10h or earlier */
++ vErrata343();
++ }
+
+ size_t car_size = car_data_size();
+ void *migrated_car = (void *)(CONFIG_RAMTOP - car_size);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0033-lib-stack-Add-stack-overrun-detection.patch b/resources/libreboot/patch/kgpe-d16/0033-lib-stack-Add-stack-overrun-detection.patch
new file mode 100644
index 00000000..eef072de
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0033-lib-stack-Add-stack-overrun-detection.patch
@@ -0,0 +1,36 @@
+From d2d52a97ccabc604e0a4ee2f43522bac2f93d47a Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:31:03 -0500
+Subject: [PATCH 033/146] lib/stack: Add stack overrun detection
+
+---
+ src/lib/stack.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/lib/stack.c b/src/lib/stack.c
+index 52dd723..bebeea2 100644
+--- a/src/lib/stack.c
++++ b/src/lib/stack.c
+@@ -18,6 +18,7 @@ it with the version available from LANL.
+ */
+ /* Copyright 2000, Ron Minnich, Advanced Computing Lab, LANL
+ * rminnich@lanl.gov
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ */
+
+ #include <assert.h>
+@@ -33,9 +34,9 @@ int checkstack(void *top_of_stack, int core)
+ u32 *stack = (u32 *) (top_of_stack - stack_size);
+
+ if (stack[0] != 0xDEADBEEF){
+- printk(BIOS_ERR, "Stack overrun on CPU%d. "
++ printk(BIOS_ERR, "Stack overrun on CPU%d (address %p overwritten). "
+ "Increase stack from current %zu bytes\n",
+- core, stack_size);
++ core, stack, stack_size);
+ BUG();
+ return -1;
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0034-cpu-x86-lapic-Add-stack-overrun-detection.patch b/resources/libreboot/patch/kgpe-d16/0034-cpu-x86-lapic-Add-stack-overrun-detection.patch
new file mode 100644
index 00000000..56ee3b5d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0034-cpu-x86-lapic-Add-stack-overrun-detection.patch
@@ -0,0 +1,32 @@
+From 1539fd430edf754b165301386b121f95c9b60791 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:31:20 -0500
+Subject: [PATCH 034/146] cpu/x86/lapic: Add stack overrun detection
+
+---
+ src/cpu/x86/lapic/lapic_cpu_init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/cpu/x86/lapic/lapic_cpu_init.c b/src/cpu/x86/lapic/lapic_cpu_init.c
+index 7fedd00..faa1f1f 100644
+--- a/src/cpu/x86/lapic/lapic_cpu_init.c
++++ b/src/cpu/x86/lapic/lapic_cpu_init.c
+@@ -5,6 +5,7 @@
+ * Copyright (C) 2001 Ronald G. Minnich
+ * Copyright (C) 2005 Yinghai Lu
+ * Copyright (C) 2008 coresystems GmbH
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -494,6 +495,7 @@ static void wait_other_cpus_stop(struct bus *cpu_bus)
+ }
+ }
+ printk(BIOS_DEBUG, "All AP CPUs stopped (%ld loops)\n", loopcount);
++ checkstack(_estack, 0);
+ for(i = 1; i <= last_cpu_index; i++)
+ checkstack((void *)stacks[i] + CONFIG_STACK_SIZE, i);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0035-southbridge-amd-sr5650-Add-AMD-Family-15h-CPU-suppor.patch b/resources/libreboot/patch/kgpe-d16/0035-southbridge-amd-sr5650-Add-AMD-Family-15h-CPU-suppor.patch
new file mode 100644
index 00000000..75fbc266
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0035-southbridge-amd-sr5650-Add-AMD-Family-15h-CPU-suppor.patch
@@ -0,0 +1,26 @@
+From 6ee3b567040bc49b93eb218cded2727c098b1d5b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:30:38 -0500
+Subject: [PATCH 035/146] southbridge/amd/sr5650: Add AMD Family 15h CPU
+ support
+
+---
+ src/southbridge/amd/sr5650/early_setup.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
+index 664f60a..62b0dab 100644
+--- a/src/southbridge/amd/sr5650/early_setup.c
++++ b/src/southbridge/amd/sr5650/early_setup.c
+@@ -94,6 +94,8 @@ static void get_cpu_rev(void)
+ printk(BIOS_INFO, "CPU Rev is K8_G1.\n");
+ else if (eax <= 0x100fa0)
+ printk(BIOS_INFO, "CPU Rev is Fam 10.\n");
++ else if (eax <= 0x600f20)
++ printk(BIOS_INFO, "CPU Rev is Fam 15.\n");
+ else
+ printk(BIOS_INFO, "CPU Rev is not recognized.\n");
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0036-cpu-amd-Add-initial-AMD-Family-15h-support.patch b/resources/libreboot/patch/kgpe-d16/0036-cpu-amd-Add-initial-AMD-Family-15h-support.patch
new file mode 100644
index 00000000..3fa31531
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0036-cpu-amd-Add-initial-AMD-Family-15h-support.patch
@@ -0,0 +1,15930 @@
+From ea0ded12622a5307ea596201fb25f64ec622a5f4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 1 Jun 2015 04:04:42 -0500
+Subject: [PATCH 036/146] cpu/amd: Add initial AMD Family 15h support
+
+TEST: Booted ASUS KGPE-D16 with single Opteron 6380
+ * Unbuffered DDR3 DIMMs tested and working
+ * Suspend to RAM (S3) tested and working
+---
+ src/cpu/amd/car/cache_as_ram.inc | 130 +-
+ src/cpu/amd/car/disable_cache_as_ram.c | 87 +-
+ src/cpu/amd/model_10xxx/defaults.h | 266 +-
+ src/cpu/amd/model_10xxx/fidvid.c | 235 +-
+ src/cpu/amd/model_10xxx/init_cpus.c | 232 +-
+ src/cpu/amd/model_10xxx/model_10xxx_init.c | 92 +-
+ src/cpu/amd/model_10xxx/powernow_acpi.c | 50 +-
+ src/cpu/amd/model_10xxx/processor_name.c | 194 +-
+ src/cpu/amd/model_10xxx/update_microcode.c | 6 +
+ src/cpu/amd/quadcore/quadcore.c | 109 +-
+ src/cpu/amd/quadcore/quadcore_id.c | 43 +-
+ src/include/cpu/amd/model_10xxx_msr.h | 7 +
+ src/northbridge/amd/amdfam10/Kconfig | 2 +-
+ src/northbridge/amd/amdfam10/Makefile.inc | 2 +
+ src/northbridge/amd/amdfam10/amdfam10.h | 6 +-
+ src/northbridge/amd/amdfam10/amdfam10_util.c | 13 +-
+ src/northbridge/amd/amdfam10/link_control.c | 86 +
+ src/northbridge/amd/amdfam10/misc_control.c | 6 +
+ src/northbridge/amd/amdfam10/nb_control.c | 85 +
+ src/northbridge/amd/amdfam10/northbridge.c | 241 +-
+ src/northbridge/amd/amdfam10/raminit_amdmct.c | 304 ++-
+ src/northbridge/amd/amdht/h3ncmn.c | 171 +-
+ src/northbridge/amd/amdht/ht_wrapper.c | 43 +-
+ src/northbridge/amd/amdmct/amddefs.h | 78 +-
+ src/northbridge/amd/amdmct/mct/mct_d.c | 4 +-
+ src/northbridge/amd/amdmct/mct/mct_d.h | 20 +-
+ src/northbridge/amd/amdmct/mct/mctpro_d.c | 21 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 3199 +++++++++++++++++++----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 124 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h | 9 +
+ src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c | 21 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c | 27 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 1087 +++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 55 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c | 7 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c | 105 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 2 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 24 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 585 ++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 1342 ++++++++--
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c | 10 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c | 20 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 255 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 1007 +++++--
+ src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c | 69 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h | 46 +-
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 652 +++--
+ src/northbridge/amd/amdmct/wrappers/mcti.h | 14 +-
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 42 +-
+ 49 files changed, 9183 insertions(+), 2052 deletions(-)
+ create mode 100644 src/northbridge/amd/amdfam10/link_control.c
+ create mode 100644 src/northbridge/amd/amdfam10/nb_control.c
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 133daac..ec70f67 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -32,18 +32,23 @@
+ #define CacheSizeAPStack CONFIG_DCACHE_AP_STACK_SIZE
+
+ #define MSR_MCFG_BASE 0xC0010058
+-#define MSR_FAM10 0xC001102A
++#define MSR_BU_CFG2 0xC001102A
+
+ #define jmp_if_k8(x) comisd %xmm2, %xmm1; jb x
++#define jmp_if_not_fam15h(x) comisd %xmm3, %xmm1; jb x
++#define jmp_if_fam15h(x) comisd %xmm3, %xmm1; jae x
+
+ #define CPUID_MASK 0x0ff00f00
+ #define CPUID_VAL_FAM10_ROTATED 0x0f000010
++#define CPUID_VAL_FAM15_ROTATED 0x0f000060
+
+ /*
+ * XMM map:
+ * xmm1: CPU family
+ * xmm2: Fam10h comparison value
+- * xmm3: Backup EBX
++ * xmm3: Fam15h comparison value
++ * xmm4: Backup EBX
++ * xmm5: Coreboot init detect
+ */
+
+ /* Save the BIST result. */
+@@ -63,7 +68,7 @@ cache_as_ram_setup:
+ movl %eax, %cr4
+
+ /* Figure out the CPU family. */
+- cvtsi2sd %ebx, %xmm3
++ cvtsi2sd %ebx, %xmm4
+ movl $0x01, %eax
+ cpuid
+ /* Base family is bits 8..11, extended family is bits 20..27. */
+@@ -73,13 +78,16 @@ cache_as_ram_setup:
+ cvtsi2sd %eax, %xmm1
+ movl $CPUID_VAL_FAM10_ROTATED, %eax
+ cvtsi2sd %eax, %xmm2
+- cvtsd2si %xmm3, %ebx
++ movl $CPUID_VAL_FAM15_ROTATED, %eax
++ cvtsi2sd %eax, %xmm3
++ cvtsd2si %xmm4, %ebx
+
+ /* Check if cpu_init_detected. */
+ movl $MTRRdefType_MSR, %ecx
+ rdmsr
+ andl $MTRRdefTypeEn, %eax
+ movl %eax, %ebx /* We store the status. */
++ cvtsi2sd %ebx, %xmm5
+
+ jmp_if_k8(CAR_FAM10_out_post_errata)
+
+@@ -120,21 +128,24 @@ cache_as_ram_setup:
+
+ CAR_FAM10_out:
+
++ jmp_if_fam15h(CAR_FAM10_errata_applied)
+ /*
+ * Errata 193: Disable clean copybacks to L3 cache to allow cached ROM.
+ * Re-enable it in after RAM is initialized and before CAR is disabled.
+ */
+- movl $MSR_FAM10, %ecx
++ movl $MSR_BU_CFG2, %ecx
+ rdmsr
+- bts $15, %eax
++ bts $15, %eax /* Set bit 15 in EDX:EAX (bit 15 in EAX). */
+ wrmsr
+
+ /* Erratum 343, RevGuide for Fam10h, Pub#41322 Rev. 3.33 */
+- movl $MSR_FAM10, %ecx
++ movl $MSR_BU_CFG2, %ecx
+ rdmsr
+ bts $35-32, %edx /* Set bit 35 in EDX:EAX (bit 3 in EDX). */
+ wrmsr
+
++CAR_FAM10_errata_applied:
++
+ #if CONFIG_MMCONF_SUPPORT
+ #if (CONFIG_MMCONF_BASE_ADDRESS > 0xFFFFFFFF)
+ #error "MMCONF_BASE_ADDRESS too big"
+@@ -169,6 +180,63 @@ CAR_FAM10_out:
+
+ CAR_FAM10_out_post_errata:
+
++ /* Fam15h APIC IDs do not depend on NB config bit 54 */
++ jmp_if_not_fam15h(skip_nb54_set)
++ movl $0xc001001f, %ecx /* NB_CFG_MSR */
++ rdmsr
++ bts $(54 - 32), %edx /* Set NB config bit 54 */
++ wrmsr
++
++skip_nb54_set:
++ /* On Fam15h CPUs each compute unit's MTRRs are shared between two cores */
++ jmp_if_not_fam15h(skip_cu_check)
++
++ /* Get the initial APIC ID. */
++ movl $1, %eax
++ cpuid
++ movl %ebx, %eax
++
++ /* Restore init detect */
++ cvtsd2si %xmm5, %ebx
++
++ /* Determine if this is the second core to start in a compute unit; if so, wait for first core start, clear init detect and skip MTRR init */
++ bt $24, %eax
++ jnc skip_cu_check /* First core in the compute unit jumps to skip_cu_check */
++
++ /* Determine if this is the second core to start in a compute unit; if so, clear init detect and skip MTRR init */
++ /* Busywait until the first core sets up the MTRRs */
++check_init_detect_1:
++ /* Check if cpu_init_detected. */
++ movl $MTRRdefType_MSR, %ecx
++ rdmsr
++ andl $MTRRdefTypeEn, %eax
++ cmp $0x00000000, %eax
++ je check_init_detect_1 /* First core has not yet started */
++
++check_init_detect_2:
++ movl $SYSCFG_MSR, %ecx
++ rdmsr
++ andl $(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrVarDramEn), %eax
++ cmp $0x00000000, %eax
++ je check_init_detect_2 /* First core has not yet started */
++
++ /* First core has now started */
++ movl $0x00000000, %ebx /* Clear init detect flag */
++ cvtsi2sd %ebx, %xmm5
++ jmp fam10_mtrr_setup_complete
++
++skip_cu_check:
++
++ jmp_if_not_fam15h(CAR_FAM15_errata_applied)
++
++ /* Erratum 714, RevGuide for Fam15h, Pub#48063 Rev. 3.24 */
++ movl $MSR_BU_CFG2, %ecx
++ rdmsr
++ bts $8, %eax /* Set bit 8 in EDX:EAX (bit 8 in EAX). */
++ wrmsr
++
++CAR_FAM15_errata_applied:
++
+ /* Set MtrrFixDramModEn for clear fixed MTRR. */
+ enable_fixed_mtrr_dram_modify:
+ movl $SYSCFG_MSR, %ecx
+@@ -337,8 +405,42 @@ wbcache_post_fam10_setup:
+ orl $(SYSCFG_MSR_MtrrVarDramEn | SYSCFG_MSR_MtrrFixDramEn), %eax
+ wrmsr
+
++fam10_mtrr_setup_complete:
+ post_code(0xa1)
+
++ /* Disable conversion of INVD to WBINVD (INVDWBINVD = 0) */
++ mov $0xc0010015, %ecx
++ rdmsr
++ btr $4, %eax
++ wrmsr
++
++jmp_if_not_fam15h(fam15_car_msr_setup_complete)
++ /* Disable streaming store (DisSS = 1) */
++ mov $0xc0011020, %ecx
++ rdmsr
++ bts $28, %eax
++ wrmsr
++
++ /* Disable speculative ITLB reloads (DisSpecTlbRld = 1) */
++ mov $0xc0011021, %ecx
++ rdmsr
++ bts $9, %eax
++ wrmsr
++
++ /* Disable speculative DTLB reloads (DisSpecTlbRld = 1) and set DisHwPf = 1 */
++ mov $0xc0011022, %ecx
++ rdmsr
++ bts $4, %eax
++ bts $13, %eax
++ wrmsr
++
++ /* Disable CR0 combining (CombineCr0Cd = 0) */
++ mov $0xc001102b, %ecx
++ rdmsr
++ btr $49-32, %edx
++ wrmsr
++fam15_car_msr_setup_complete:
++
+ /* Enable cache. */
+ movl %cr0, %eax
+ andl $(~(CR0_CacheDisable | CR0_NoWriteThrough)), %eax
+@@ -393,9 +495,6 @@ CAR_FAM10_ap:
+ * to reverse it.
+ */
+
+- /* Store our init detected. */
+- movl %ebx, %esi
+-
+ /* Get the coreid bits at first. */
+ movl $0x80000008, %eax
+ cpuid
+@@ -414,6 +513,8 @@ CAR_FAM10_ap:
+ movl %edi, %ecx /* CoreID bits */
+ bt $(54 - 32), %edx
+ jc roll_cfg
++
++ /* Fam10h NB config bit 54 was not set */
+ rolb %cl, %bl
+ roll_cfg:
+
+@@ -423,8 +524,8 @@ roll_cfg:
+ movl $(CacheBase + (CacheSize - (CacheSizeBSPStack + CacheSizeBSPSlush))), %esp
+ subl %eax, %esp
+
+- /* Retrive init detected. */
+- movl %esi, %ebx
++ /* Restore init detect */
++ cvtsd2si %xmm5, %ebx
+
+ post_code(0xa4)
+
+@@ -437,6 +538,8 @@ CAR_FAM10_ap_out:
+ andl $~(3 << 9), %eax
+ movl %eax, %cr4
+
++ post_code(0xa6)
++
+ /* Restore the BIST result. */
+ movl %ebp, %eax
+
+@@ -444,6 +547,9 @@ CAR_FAM10_ap_out:
+ movl %esp, %ebp
+ pushl %ebx /* Init detected. */
+ pushl %eax /* BIST */
++
++ post_code(0xa7)
++
+ call cache_as_ram_main
+
+ /* We will not go back. */
+diff --git a/src/cpu/amd/car/disable_cache_as_ram.c b/src/cpu/amd/car/disable_cache_as_ram.c
+index d3a3812..8a2a0ca 100644
+--- a/src/cpu/amd/car/disable_cache_as_ram.c
++++ b/src/cpu/amd/car/disable_cache_as_ram.c
+@@ -19,46 +19,93 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ *
+- * be warned, this file will be used other cores and core 0 / node 0
++ * WARNING: this file will be used by both any AP cores and core 0 / node 0
+ */
+
+ #include <cpu/x86/cache.h>
+
+-static inline __attribute__((always_inline)) void disable_cache_as_ram(void)
++static inline __attribute__((always_inline)) uint32_t amd_fam1x_cpu_family(void)
++{
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ return family;
++}
++
++static inline __attribute__((always_inline)) void disable_cache_as_ram(uint8_t skip_sharedc_config)
+ {
+ msr_t msr;
++ uint32_t family;
+
+- /* disable cache */
+- write_cr0(read_cr0() | CR0_CacheDisable);
++ if (!skip_sharedc_config) {
++ /* disable cache */
++ write_cr0(read_cr0() | CR0_CacheDisable);
+
+- msr.lo = 0;
+- msr.hi = 0;
+- wrmsr(MTRRfix4K_C8000_MSR, msr);
++ msr.lo = 0;
++ msr.hi = 0;
++ wrmsr(MTRRfix4K_C8000_MSR, msr);
+ #if CONFIG_DCACHE_RAM_SIZE > 0x8000
+- wrmsr(MTRRfix4K_C0000_MSR, msr);
++ wrmsr(MTRRfix4K_C0000_MSR, msr);
+ #endif
+ #if CONFIG_DCACHE_RAM_SIZE > 0x10000
+- wrmsr(MTRRfix4K_D0000_MSR, msr);
++ wrmsr(MTRRfix4K_D0000_MSR, msr);
+ #endif
+ #if CONFIG_DCACHE_RAM_SIZE > 0x18000
+- wrmsr(MTRRfix4K_D8000_MSR, msr);
++ wrmsr(MTRRfix4K_D8000_MSR, msr);
+ #endif
+- /* disable fixed mtrr from now on, it will be enabled by ramstage again*/
++ /* disable fixed mtrr from now on, it will be enabled by ramstage again */
++ msr = rdmsr(SYSCFG_MSR);
++ msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn);
++ wrmsr(SYSCFG_MSR, msr);
++
++ /* Set the default memory type and disable fixed and enable variable MTRRs */
++ msr.hi = 0;
++ msr.lo = (1 << 11);
++
++ wrmsr(MTRRdefType_MSR, msr);
++
++ enable_cache();
++ }
++
++ /* INVDWBINVD = 1 */
++ msr = rdmsr(0xc0010015);
++ msr.lo |= (0x1 << 4);
++ wrmsr(0xc0010015, msr);
++
++ family = amd_fam1x_cpu_family();
++
++ if (family >= 0x6f) {
++ /* Family 15h or later */
+
+- msr = rdmsr(SYSCFG_MSR);
+- msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn);
+- wrmsr(SYSCFG_MSR, msr);
++ /* DisSS = 0 */
++ msr = rdmsr(0xc0011020);
++ msr.lo &= ~(0x1 << 28);
++ wrmsr(0xc0011020, msr);
+
+- /* Set the default memory type and disable fixed and enable variable MTRRs */
+- msr.hi = 0;
+- msr.lo = (1 << 11);
++ if (!skip_sharedc_config) {
++ /* DisSpecTlbRld = 0 */
++ msr = rdmsr(0xc0011021);
++ msr.lo &= ~(0x1 << 9);
++ wrmsr(0xc0011021, msr);
+
+- wrmsr(MTRRdefType_MSR, msr);
++ /* Erratum 714: SpecNbReqDis = 0 */
++ msr = rdmsr(BU_CFG2_MSR);
++ msr.lo &= ~(0x1 << 8);
++ wrmsr(BU_CFG2_MSR, msr);
++ }
+
+- enable_cache();
++ /* DisSpecTlbRld = 0 */
++ /* DisHwPf = 0 */
++ msr = rdmsr(0xc0011022);
++ msr.lo &= ~(0x1 << 4);
++ msr.lo &= ~(0x1 << 13);
++ wrmsr(0xc0011022, msr);
++ }
+ }
+
+ static void disable_cache_as_ram_bsp(void)
+ {
+- disable_cache_as_ram();
++ disable_cache_as_ram(0);
+ }
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 6fd1a7e..24f87ba 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -25,41 +26,65 @@
+ */
+ static const struct {
+ u32 msr;
+- u32 revision;
++ uint64_t revision;
+ u32 platform;
+ u32 data_lo;
+ u32 data_hi;
+ u32 mask_lo;
+ u32 mask_hi;
+ } fam10_msr_default[] = {
+- { TOP_MEM2, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { TOP_MEM2, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF },
+
+- { SYSCFG, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { SYSCFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 3 << 21, 0x00000000,
+ 3 << 21, 0x00000000 }, /* [MtrrTom2En]=1,[TOM2EnWB] = 1*/
+
+- { HWCR, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 1 << 4, 0x00000000,
+- 1 << 4, 0x00000000 }, /* [INVD_WBINVD]=1 */
++ { MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL,
++ 1 << 18, 0x00000000,
++ 1 << 18, 0x00000000 }, /* Erratum 586: [DEIBP]=1 */
+
+- { MC4_CTL_MASK, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL,
++ 1 << 15, 0x00000000,
++ 1 << 15, 0x00000000 }, /* Erratum 593: [BSRP]=1 */
++
++ { MC1_CTL_MASK, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 15, 0x00000000,
++ 1 << 15, 0x00000000 }, /* Erratum 739: [BSRP]=1 */
++
++ { 0xc0011000, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 1 << 16, 0x00000000,
++ 1 << 16, 0x00000000 }, /* Erratum 608: [bit 16]=1 */
++
++ { 0xc0011000, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 15, 0x00000000,
++ 1 << 15, 0x00000000 }, /* Erratum 727: [bit 15]=1 */
++
++ { MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0xF << 19, 0x00000000,
+ 0xF << 19, 0x00000000 }, /* [RtryHt[0..3]]=1 */
+
++ { MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 1 << 10, 0x00000000,
++ 1 << 10, 0x00000000 }, /* [GartTblWkEn]=1 */
++
+ { DC_CFG, AMD_FAM10_ALL, AMD_PTYPE_SVR,
+ 0x00000000, 0x00000004,
+- 0x00000000, 0x0000000C }, /* [REQ_CTR] = 1 for Server */
++ 0x00000000, 0x0000000C }, /* Family 10h: [REQ_CTR] = 1 for Server */
+
+ { DC_CFG, AMD_DR_Bx, AMD_PTYPE_SVR,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000C00 }, /* Erratum 326 */
+
+- { NB_CFG, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
++ { NB_CFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC,
+ 0x00000000, 1 << 22,
+ 0x00000000, 1 << 22 }, /* [ApicInitIDLo]=1 */
+
++ { NB_CFG, AMD_FAM15_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
++ 1 << 23, 0x00000000,
++ 1 << 23, 0x00000000 }, /* Erratum 663: [bit 23]=1 */
++
+ { BU_CFG2, AMD_DR_Bx, AMD_PTYPE_ALL,
+ 1 << 29, 0x00000000,
+ 1 << 29, 0x00000000 }, /* For Bx Smash1GPages=1 */
+@@ -72,6 +97,14 @@ static const struct {
+ 0 << 1, 0x00000000,
+ 1 << 1, 0x00000000 }, /* IDX_MATCH_ALL=0 */
+
++ { IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
++ 0x00000000, 1 << (39-32),
++ 0x00000000, 1 << (39-32)}, /* C0 or above [DisLoopPredictor]=1 */
++
++ { IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
++ 0xf << 1, 0x00000000,
++ 0xf << 1, 0x00000000}, /* C0 or above [DisIcWayFilter]=0xf */
++
+ { BU_CFG, AMD_DR_LT_B3, AMD_PTYPE_ALL,
+ 1 << 21, 0x00000000,
+ 1 << 21, 0x00000000 }, /* Erratum #254 DR B1 BU_CFG[21]=1 */
+@@ -80,19 +113,51 @@ static const struct {
+ 1 << 23, 0x00000000,
+ 1 << 23, 0x00000000 }, /* Erratum #309 BU_CFG[23]=1 */
+
++ { BU_CFG, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0 << 10, 0x00000000,
++ 1 << 10, 0x00000000 }, /* [DcacheAgressivePriority]=0 */
++
+ /* CPUID_EXT_FEATURES */
+- { CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
++ { CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC,
+ 1 << 28, 0x00000000,
+ 1 << 28, 0x00000000 }, /* [HyperThreadFeatEn]=1 */
+
+- { CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC,
++ { CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC,
+ 0x00000000, 1 << (33-32),
+ 0x00000000, 1 << (33-32) }, /* [ExtendedFeatEn]=1 */
+
++ { DE_CFG, AMD_OR_B2, AMD_PTYPE_ALL,
++ 1 << 10, 0x00000000,
++ 1 << 10, 0x00000000 }, /* Bx [ResyncPredSingleDispDis]=1 */
++
+ { BU_CFG2, AMD_DRBH_Cx, AMD_PTYPE_ALL,
+ 0x00000000, 1 << (35-32),
+ 0x00000000, 1 << (35-32) }, /* Erratum 343 (set to 0 after CAR, in post_cache_as_ram()/model_10xxx_init() ) */
+
++ { BU_CFG3, AMD_OR_B2, AMD_PTYPE_ALL,
++ 0x00000000, 1 << (42-32),
++ 0x00000000, 1 << (42-32)}, /* Bx [PwcDisableWalkerSharing]=1 */
++
++ { BU_CFG3, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 22, 0x00000000,
++ 1 << 22, 0x00000000}, /* C0 or above [PfcDoubleStride]=1 */
++
++ { EX_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
++ 0x00000000, 1 << (54-32),
++ 0x00000000, 1 << (54-32)}, /* C0 or above [LateSbzResync]=1 */
++
++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 23, 0x00000000,
++ 1 << 23, 0x00000000}, /* C0 or above [DisScbThreshold]=1 */
++
++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 14, 0x00000000,
++ 1 << 14, 0x00000000}, /* C0 or above [ForceSmcCheckFlowStDis]=1 */
++
++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
++ 1 << 12, 0x00000000,
++ 1 << 12, 0x00000000}, /* C0 or above [ForceBusLockDis]=1 */
++
+ { OSVW_ID_Length, AMD_DR_Bx | AMD_DR_Cx | AMD_DR_Dx, AMD_PTYPE_ALL,
+ 0x00000004, 0x00000000,
+ 0x00000004, 0x00000000}, /* B0 or Above, OSVW_ID_Length is 0004h */
+@@ -105,9 +170,45 @@ static const struct {
+ 0x00000000, 1 << (50-32),
+ 0x00000000, 1 << (50-32)}, /* D0 or Above, RdMmExtCfgQwEn*/
+
++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x0 << (36-32),
++ 0x00000000, 0x3 << (36-32)}, /* [ThrottleNbInterface]=0 */
++
++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 1 << 10, 0x00000000,
++ 1 << 10, 0x00000000}, /* [VicResyncChkEn]=1 */
++
++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 1 << 11, 0x00000000,
++ 1 << 11, 0x00000000}, /* Erratum 503: [bit 11]=1 */
++
+ { CPU_ID_EXT_FEATURES_MSR, AMD_DR_Dx, AMD_PTYPE_ALL,
+ 0x00000000, 1 << (51 - 32),
+ 0x00000000, 1 << (51 - 32)}, /* G34_PKG | C32_PKG | S1G4_PKG | ASB2_PKG */
++
++ { CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 1 << (56 - 32),
++ 0x00000000, 1 << (56 - 32)}, /* [PerfCtrExtNB]=1 */
++
++ { CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 1 << (55 - 32),
++ 0x00000000, 1 << (55 - 32)}, /* [PerfCtrExtCore]=1 */
++
++ { IBS_OP_DATA3, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0 << 16, 0x00000000,
++ 1 << 16, 0x00000000}, /* [IbsDcMabHit]=0 */
++
++ { MC4_MISC0, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x1 << (52-32),
++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */
++
++ { MC4_MISC1, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x1 << (52-32),
++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */
++
++ { MC4_MISC2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x1 << (52-32),
++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */
+ };
+
+
+@@ -117,37 +218,46 @@ static const struct {
+ static const struct {
+ u8 function;
+ u16 offset;
+- u32 revision;
++ uint64_t revision;
+ u32 platform;
+ u32 data;
+ u32 mask;
+ } fam10_pci_default[] = {
+
+ /* Function 0 - HT Config */
++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 0x000e0000, 0x000e0000 }, /* [19:17] for 8bit APIC config */
++
++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 0x00400000, 0x00600000 }, /* [22:21] DsNpReqLmt = 10b */
+
+- { 0, 0x68, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x004E4800, 0x006E6800 }, /* [19:17] for 8bit APIC config,
+- [14:13] BufPriRel = 2h [11] RspPassPW set,
+- [22:21] DsNpReqLmt = 10b */
++ { 0, 0x68, AMD_FAM10_LT_D, AMD_PTYPE_ALL,
++ 0x00004000, 0x00006000 }, /* [14:13] BufRelPri = 2h */
++
++ { 0, 0x68, (AMD_FAM10_REV_D | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 0x00002000, 0x00006000 }, /* [14:13] BufRelPri = 1h */
++
++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 0x00000800, 0x00000800 }, /* [11] RspPassPW = 1 */
+
+ /* Errata 281 Workaround */
+ { 0, 0x68, (AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00200000, 0x00600000 }, /* [22:21] DsNpReqLmt0 = 01b */
+
+- { 0, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 0, 0x84, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 0, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xC4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 0, 0xC4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xE4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 0, 0xE4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+ /* Link Global Retry Control Register */
+- { 0, 0x150, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00073900, 0x00073F00 },
+
+ /* Errata 351
+@@ -172,13 +282,39 @@ static const struct {
+ 0x00000000, 0x00000100 },
+ { 0, 0x18C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00000000, 0x00000100 },
+- { 0, 0x170, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x00000000, 0x00000100 },
+
+ /* Link Global Extended Control Register */
+ { 0, 0x16C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00000014, 0x0000003F }, /* [15:13] ForceFullT0 = 0b,
+- * Set T0Time 14h per BKDG */
++ * Set T0Time 14h per BKDG */
++
++ { 0, 0x170, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x174, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x178, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x17C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x180, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x184, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x188, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++ { 0, 0x18C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000100, 0x00000100 },
++
++ /* Link Global Extended Control Register */
++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000014, 0x0000003F }, /* [15:13] ForceFullT0 = 111b,
++ * Set T0Time 26h per BKDG */
++
++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x7 << 13, 0x7 << 13 }, /* [15:13] ForceFullT0 = 7h */
++
++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x26, 0x3f }, /* [5:0] T0Time = 26h */
+
+
+ /* Function 1 - Map Init */
+@@ -205,10 +341,10 @@ static const struct {
+ /* Function 2 - DRAM Controller */
+
+ /* Function 3 - Misc. Control */
+- { 3, 0x40, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0x40, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00000100, 0x00000100 }, /* [8] MstrAbrtEn */
+
+- { 3, 0x44, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0x44, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x4A30005C, 0x4A30005C }, /* [30] SyncOnDramAdrParErrEn = 1,
+ [27] NbMcaToMstCpuEn = 1,
+ [25] DisPciCfgCpuErrRsp = 1,
+@@ -220,8 +356,12 @@ static const struct {
+ [2] SyncOnUcEccEn = 1 */
+
+ /* XBAR buffer settings */
+- { 3, 0x6C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x00018052, 0x700780F7 },
++ { 3, 0x6c, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ 0x00018052, 0x700780f7 },
++
++ /* XBAR buffer settings */
++ { 3, 0x6c, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x10010052, 0x700700f7 },
+
+ /* Errata 281 Workaround */
+ { 3, 0x6C, ( AMD_DR_B0 | AMD_DR_B1),
+@@ -233,12 +373,18 @@ static const struct {
+ { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00041153, 0x777777F7 },
+
++ { 3, 0x70, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x10171155, 0x777777f7 },
++
+ { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x61221151, 0x777777F7 },
+
+ { 3, 0x74, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00080101, 0x000F7777 },
+
++ { 3, 0x74, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00172111, 0x77ff7777 },
++
+ { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00090914, 0x707FFF1F },
+
+@@ -246,12 +392,18 @@ static const struct {
+ { 3, 0x7C, ( AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00144514, 0x707FFF1F },
+
++ { 3, 0x7C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x040d0f16, 0x07ffff1f },
++
+ { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00070814, 0x007FFF1F },
+
+ { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00800756, 0x00F3FFFF },
+
++ { 3, 0x140, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00a11755, 0x00f3ffff },
++
+ { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00C37756, 0x00F3FFFF },
+
+@@ -263,6 +415,9 @@ static const struct {
+ AMD_PTYPE_SVR, 0x00000001, 0x0000000F },
+ /* [3:0] RspTok = 0001b */
+
++ { 3, 0x144, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000028, 0x000000ff },
++
+ { 3, 0x148, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x8000052A, 0xD5FFFFFF },
+
+@@ -270,41 +425,53 @@ static const struct {
+ { 3, 0x80, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0xE6002200, 0xFFFFFFFF },
+
++ /* ACPI Power State Control Reg1 */
++ { 3, 0x80, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0xe20be200, 0xefefef00 },
++
+ /* ACPI Power State Control Reg2 */
+ { 3, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0xA0E641E6, 0xFFFFFFFF },
+
++ /* ACPI Power State Control Reg2 */
++ { 3, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x01e200e2, 0xefef00ef },
++
+ { 3, 0xA0, AMD_FAM10_ALL, AMD_PTYPE_MOB | AMD_PTYPE_DSK,
+ 0x00000080, 0x00000080 }, /* [7] PSIVidEnable */
+
+ { 3, 0xA0, AMD_DR_Bx, AMD_PTYPE_ALL,
+ 0x00002800, 0x000003800 }, /* [13:11] PllLockTime = 5 */
+
+- { 3, 0xA0, (AMD_FAM10_ALL & ~(AMD_DR_Bx)), AMD_PTYPE_ALL,
++ { 3, 0xA0, ((AMD_FAM10_ALL | AMD_FAM15_ALL) & ~(AMD_DR_Bx)), AMD_PTYPE_ALL,
+ 0x00000800, 0x000003800 }, /* [13:11] PllLockTime = 1 */
+
+ /* Reported Temp Control Register */
+- { 3, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00000080, 0x00000080 }, /* [7] TempSlewDnEn = 1 */
+
+ /* Clock Power/Timing Control 0 Register */
+- { 3, 0xD4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0xD4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0xC0000F00, 0xF0000F00 }, /* [31] NbClkDivApplyAll = 1,
+ [30:28] NbClkDiv = 100b,[11:8] ClkRampHystSel = 1111b */
+
+ /* Clock Power/Timing Control 1 Register */
++ { 3, 0xD8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ 0x03000010, 0x0F000070 }, /* [6:4] VSRampTime = 1,
++ * [27:24] ReConDel = 3 */
++
++ /* Clock Power/Timing Control 1 Register */
+ { 3, 0xD8, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x03000016, 0x0F000077 }, /* [6:4] VSRampTime = 1,
+- [2:0] VSSlamTime = 6, [27:24] ReConDel = 3 */
++ 0x00000006, 0x00000007 }, /* [2:0] VSSlamTime = 6 */
+
+
+ /* Clock Power/Timing Control 2 Register */
+- { 3, 0xDC, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0xDC, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00005000, 0x00007000 }, /* [14:12] NbsynPtrAdj = 5 */
+
+
+ /* Extended NB MCA Config Register */
+- { 3, 0x180, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0x180, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x007003E2, 0x007003E2 }, /* [22:20] = SyncFloodOn_Err = 7,
+ [9] SyncOnUncNbAryEn = 1 ,
+ [8] SyncOnProtEn = 1,
+@@ -319,12 +486,17 @@ static const struct {
+ 0x00400000, 0x00400000 },
+
+ /* L3 Control Register */
+- { 3, 0x1B8, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0x1b8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00001000, 0x00001000 }, /* [12] = L3PrivReplEn */
+
+ /* IBS Control Register */
+- { 3, 0x1CC, AMD_FAM10_ALL, AMD_PTYPE_ALL,
++ { 3, 0x1cc, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00000100, 0x00000100 }, /* [8] = LvtOffsetVal */
++
++ /* Erratum 619 - Family 15h Bx
++ * System software should set F5x88[14] to 1b. */
++ { 5, 0x88, AMD_OR_B2, AMD_PTYPE_ALL,
++ 1 << 14, 1 << 14 },
+ };
+
+
+@@ -333,7 +505,7 @@ static const struct {
+ */
+ static const struct {
+ u16 htreg; /* HT Phy Register index */
+- u32 revision;
++ uint64_t revision;
+ u32 platform;
+ u32 linktype;
+ u32 data;
+@@ -442,38 +614,38 @@ static const struct {
+ { 0x530A, AMD_DR_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x00004400, 0x00006400 }, /* HT_PHY_DLL_REG */
+
+- { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+- { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+ /* Link Phy Receiver Loop Filter Registers */
+- { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC0, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
++ { 0xC0, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+ [20:16] RttIndex = 04h */
+ };
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 99ffcc8..2e26645 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -44,7 +44,7 @@ Fam10 Bios and Kernel Development Guide #31116, rev 3.48, April 22, 2010
+
+ 3.- 2.4.2.7 dualPlaneOnly(dev)
+
+-4.- 2.4.2.8 applyBoostFIDOffset(dev)
++4.- 2.4.2.8 applyBoostFIDOffset(dev, nodeid)
+
+ 5.- enableNbPState1(dev)
+
+@@ -143,25 +143,33 @@ static void enable_fid_change(u8 fid)
+ }
+ }
+
+-static void applyBoostFIDOffset( device_t dev ) {
+- // BKDG 2.4.2.8
+- // revision E only, but E is apparently not supported yet, therefore untested
+- if ((cpuid_edx(0x80000007) & CPB_MASK)
+- && ((cpuid_ecx(0x80000008) & NC_MASK) ==5) ) {
+- u32 core = get_node_core_id_x().coreid;
+- u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3;
+- msr_t msr = rdmsr(PS_REG_BASE);
+- u32 cpuFid = msr.lo & PS_CPU_FID_MASK;
+- cpuFid = cpuFid + asymetricBoostThisCore;
+- msr.lo &= ~PS_CPU_FID_MASK;
+- msr.lo |= cpuFid ;
+- wrmsr(PS_REG_BASE , msr);
+-
+- }
++static void applyBoostFIDOffset(device_t dev, uint32_t nodeid) {
++ // BKDG 2.4.2.8
++ // Fam10h revision E only, but E is apparently not supported yet, therefore untested
++ if ((cpuid_edx(0x80000007) & CPB_MASK)
++ && ((cpuid_ecx(0x80000008) & NC_MASK) == 5) ) {
++ u32 core = get_node_core_id_x().coreid;
++ u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3;
++ msr_t msr = rdmsr(PS_REG_BASE);
++ u32 cpuFid = msr.lo & PS_CPU_FID_MASK;
++ cpuFid = cpuFid + asymetricBoostThisCore;
++ msr.lo &= ~PS_CPU_FID_MASK;
++ msr.lo |= cpuFid ;
++ wrmsr(PS_REG_BASE , msr);
++ } else if (is_fam15h()) {
++ uint32_t dword = pci_read_config32(NODE_PCI(nodeid, 4), 0x15c);
++ uint8_t boost_count = (dword >> 2) & 0x7;
++ if (boost_count > 0) {
++ /* Enable boost */
++ dword &= ~0x3;
++ dword |= 0x1;
++ pci_write_config32(NODE_PCI(nodeid, 4), 0x15c, dword);
++ }
++ }
+ }
+
+ static void enableNbPState1( device_t dev ) {
+- u32 cpuRev = mctGetLogicalCPUID(0xFF);
++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
+ if (cpuRev & AMD_FAM10_C3) {
+ u32 nbPState = (pci_read_config32(dev, 0x1F0) & NB_PSTATE_MASK);
+ if ( nbPState){
+@@ -203,7 +211,7 @@ static u8 setPStateMaxVal( device_t dev ) {
+ static void dualPlaneOnly( device_t dev ) {
+ // BKDG 2.4.2.7
+
+- u32 cpuRev = mctGetLogicalCPUID(0xFF);
++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
+ if ((mctGetProcessorPackageType() == AMD_PKGTYPE_AM3_2r2)
+ && (cpuRev & AMD_DR_Cx)) { // should be rev C or rev E but there's no constant for E
+ if ( (pci_read_config32(dev, 0x1FC) & DUAL_PLANE_ONLY_MASK)
+@@ -283,12 +291,16 @@ static void recalculateVsSlamTimeSettingOnCorePre(device_t dev)
+ */
+
+ /* Determine if this is a PVI or SVI system */
+- dtemp = pci_read_config32(dev, 0xA0);
+-
+- if (dtemp & PVI_MODE)
+- pviModeFlag = 1;
+- else
++ if (is_fam15h()) {
+ pviModeFlag = 0;
++ } else {
++ dtemp = pci_read_config32(dev, 0xa0);
++
++ if (dtemp & PVI_MODE)
++ pviModeFlag = 1;
++ else
++ pviModeFlag = 0;
++ }
+
+ /* Get P0's voltage */
+ /* MSRC001_00[68:64] are not programmed yet when called from
+@@ -515,59 +527,67 @@ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
+ }
+
+ static void config_acpi_pwr_state_ctrl_regs(device_t dev, u32 cpuRev, u8 procPkg) {
+- /* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */
+- u32 dword;
+- u32 c1= 1;
+- if (cpuRev & (AMD_DR_Bx)) {
+- // will coreboot ever enable cache scrubbing ?
+- // if it does, will it be enough to check the current state
+- // or should we configure for what we'll set up later ?
+- dword = pci_read_config32(dev, 0x58);
+- u32 scrubbingCache = dword &
+- ( (0x1F << 16) // DCacheScrub
+- | (0x1F << 8) ); // L2Scrub
+- if (scrubbingCache) {
+- c1 = 0x80;
+- } else {
+- c1 = 0xA0;
+- }
+- } else { // rev C or later
+- // same doubt as cache scrubbing: ok to check current state ?
+- dword = pci_read_config32(dev, 0xDC);
+- u32 cacheFlushOnHalt = dword & (7 << 16);
+- if (!cacheFlushOnHalt) {
+- c1 = 0x80;
+- }
+- }
+- dword = (c1 << 24) | (0xE641E6);
+- pci_write_config32(dev, 0x84, dword);
+-
+-
+- /* FIXME: BKDG Table 100 says if the link is at a Gen1
+-frequency and the chipset does not support a 10us minimum LDTSTOP
+-assertion time, then { If ASB2 && SVI then smaf001 = F6h else
+-smaf001=87h. } else ... I hardly know what it means or how to check
+-it from here, so I bluntly assume it is false and code here the else,
+-which is easier */
+-
+- u32 smaf001 = 0xE6;
+- if (cpuRev & AMD_DR_Bx ) {
+- smaf001 = 0xA6;
+- } else {
+- #if CONFIG_SVI_HIGH_FREQ
+- if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) {
+- smaf001 = 0xF6;
+- }
+- #endif
+- }
+- u32 fidvidChange = 0;
+- if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX))
+- || (cpuRev & AMD_RB_C3) ) {
+- fidvidChange=0x0B;
+- }
+- dword = (0xE6 << 24) | (fidvidChange << 16)
+- | (smaf001 << 8) | 0x81;
+- pci_write_config32(dev, 0x80, dword);
++ if (is_fam15h()) {
++ /* Family 15h BKDG Rev. 3.14 D18F3x80 recommended settings */
++ pci_write_config32(dev, 0x80, 0xe20be281);
++
++ /* Family 15h BKDG Rev. 3.14 D18F3x84 recommended settings */
++ pci_write_config32(dev, 0x84, 0x01e200e2);
++ } else {
++ /* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */
++ u32 dword;
++ u32 c1= 1;
++ if (cpuRev & (AMD_DR_Bx)) {
++ // will coreboot ever enable cache scrubbing ?
++ // if it does, will it be enough to check the current state
++ // or should we configure for what we'll set up later ?
++ dword = pci_read_config32(dev, 0x58);
++ u32 scrubbingCache = dword &
++ ( (0x1F << 16) // DCacheScrub
++ | (0x1F << 8) ); // L2Scrub
++ if (scrubbingCache) {
++ c1 = 0x80;
++ } else {
++ c1 = 0xA0;
++ }
++ } else { // rev C or later
++ // same doubt as cache scrubbing: ok to check current state ?
++ dword = pci_read_config32(dev, 0xDC);
++ u32 cacheFlushOnHalt = dword & (7 << 16);
++ if (!cacheFlushOnHalt) {
++ c1 = 0x80;
++ }
++ }
++ dword = (c1 << 24) | (0xE641E6);
++ pci_write_config32(dev, 0x84, dword);
++
++ /* FIXME: BKDG Table 100 says if the link is at a Gen1
++ * frequency and the chipset does not support a 10us minimum LDTSTOP
++ * assertion time, then { If ASB2 && SVI then smaf001 = F6h else
++ * smaf001=87h. } else ... I hardly know what it means or how to check
++ * it from here, so I bluntly assume it is false and code here the else,
++ * which is easier
++ */
++
++ u32 smaf001 = 0xE6;
++ if (cpuRev & AMD_DR_Bx ) {
++ smaf001 = 0xA6;
++ } else {
++ #if CONFIG_SVI_HIGH_FREQ
++ if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) {
++ smaf001 = 0xF6;
++ }
++ #endif
++ }
++ u32 fidvidChange = 0;
++ if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX))
++ || (cpuRev & AMD_RB_C3) ) {
++ fidvidChange=0x0B;
++ }
++ dword = (0xE6 << 24) | (fidvidChange << 16)
++ | (smaf001 << 8) | 0x81;
++ pci_write_config32(dev, 0x80, dword);
++ }
+ }
+
+ static void prep_fid_change(void)
+@@ -584,7 +604,7 @@ static void prep_fid_change(void)
+ for (i = 0; i < nodes; i++) {
+ printk(BIOS_DEBUG, "Prep FID/VID Node:%02x\n", i);
+ dev = NODE_PCI(i, 3);
+- u32 cpuRev = mctGetLogicalCPUID(0xFF) ;
++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF) ;
+ u8 procPkg = mctGetProcessorPackageType();
+
+ setVSRamp(dev);
+@@ -612,7 +632,7 @@ static void prep_fid_change(void)
+ }
+ }
+
+-static void waitCurrentPstate(u32 target_pstate){
++static void waitCurrentPstate(u32 target_pstate) {
+ msr_t initial_msr = rdmsr(TSC_MSR);
+ msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
+ msr_t tsc_msr;
+@@ -645,7 +665,7 @@ static void waitCurrentPstate(u32 target_pstate){
+
+ if (pstate_msr.lo != target_pstate) {
+ msr_t limit_msr = rdmsr(0xc0010061);
+- printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%02x\n", target_pstate, pstate_msr.lo, limit_msr.lo);
++ printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n", target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo);
+
+ do { // should we just go on instead ?
+ pstate_msr = rdmsr(CUR_PSTATE_MSR);
+@@ -655,6 +675,7 @@ static void waitCurrentPstate(u32 target_pstate){
+
+ static void set_pstate(u32 nonBoostedPState) {
+ msr_t msr;
++ uint8_t skip_wait;
+
+ // Transition P0 for calling core.
+ msr = rdmsr(0xC0010062);
+@@ -662,12 +683,21 @@ static void set_pstate(u32 nonBoostedPState) {
+ msr.lo = nonBoostedPState;
+ wrmsr(0xC0010062, msr);
+
+- /* Wait for P0 to set. */
+- waitCurrentPstate(nonBoostedPState);
+-}
+-
+-
++ if (is_fam15h()) {
++ /* Do not wait for the first (even) set of cores to transition on Family 15h systems */
++ if ((cpuid_ebx(0x00000001) & 0x01000000))
++ skip_wait = 0;
++ else
++ skip_wait = 1;
++ } else {
++ skip_wait = 0;
++ }
+
++ if (!skip_wait) {
++ /* Wait for core to transition to P0 */
++ waitCurrentPstate(nonBoostedPState);
++ }
++}
+
+ static void UpdateSinglePlaneNbVid(void)
+ {
+@@ -757,11 +787,14 @@ static u32 needs_NB_COF_VID_update(void)
+ u8 nodes;
+ u8 i;
+
++ if (is_fam15h())
++ return 0;
++
+ /* If any node has nb_cof_vid_update set all nodes need an update. */
+ nodes = get_nodes();
+ nb_cof_vid_update = 0;
+ for (i = 0; i < nodes; i++) {
+- u32 cpuRev = mctGetLogicalCPUID(i) ;
++ uint64_t cpuRev = mctGetLogicalCPUID(i);
+ u32 nbCofVidUpdateDefined = (cpuRev & (AMD_FAM10_LT_D));
+ if (nbCofVidUpdateDefined
+ && (pci_read_config32(NODE_PCI(i, 3), 0x1FC)
+@@ -785,9 +818,11 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid)
+ /* Steps 1-6 of BIOS NB COF and VID Configuration
+ * for SVI and Single-Plane PVI Systems. BKDG 2.4.2.9 #31116 rev 3.48
+ */
+-
+ dev = NODE_PCI(nodeid, 3);
+- pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE;
++ if (is_fam15h())
++ pvimode = 0;
++ else
++ pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE;
+ reg1fc = pci_read_config32(dev, 0x1FC);
+
+ if (nb_cof_vid_update) {
+@@ -799,7 +834,7 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid)
+ fid_max = fid_max + ((reg1fc & DUAL_PLANE_NB_FID_OFF_MASK ) >> DUAL_PLANE_NB_FID_SHIFT );
+ }
+ /* write newNbVid to P-state Reg's NbVid always if NbVidUpdatedAll=1 */
+- fixPsNbVidBeforeWR(vid_max, coreid,dev,pvimode);
++ fixPsNbVidBeforeWR(vid_max, coreid, dev, pvimode);
+
+ /* fid setup is handled by the BSP at the end. */
+
+@@ -819,7 +854,7 @@ static void init_fidvid_ap(u32 apicid, u32 nodeid, u32 coreid)
+
+ printk(BIOS_DEBUG, "FIDVID on AP: %02x\n", apicid);
+
+- send = init_fidvid_core(nodeid,coreid);
++ send = init_fidvid_core(nodeid, coreid);
+ send |= (apicid << 24); // ap apicid
+
+ // Send signal to BSP about this AP max fid
+@@ -861,7 +896,7 @@ static void init_fidvid_bsp_stage1(u32 ap_apicid, void *gp)
+ while (--loop > 0) {
+ if (lapic_remote_read(ap_apicid, LAPIC_MSG_REG, &readback) != 0)
+ continue;
+- if ((readback & 0x3f) == 1) {
++ if (((readback & 0x3f) == 1) || ((readback & 0x3f) == F10_APSTATE_ASLEEP)) {
+ timeout = 0;
+ break; /* target ap is in stage 1 */
+ }
+@@ -949,7 +984,10 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid)
+ /* If any node has nb_cof_vid_update set all nodes need an update. */
+
+ dev = NODE_PCI(nodeid, 3);
+- pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1;
++ if (is_fam15h())
++ pvimode = 0;
++ else
++ pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1;
+ reg1fc = pci_read_config32(dev, 0x1FC);
+ nbvid = (reg1fc >> 7) & 0x7F;
+ NbVidUpdateAll = (reg1fc >> 1) & 1;
+@@ -970,15 +1008,17 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid)
+ pci_write_config32(dev, 0xA0, dtemp);
+
+ dualPlaneOnly(dev);
+- applyBoostFIDOffset(dev);
++ applyBoostFIDOffset(dev, nodeid);
+ enableNbPState1(dev);
+
+ finalPstateChange();
+
+- /* Set TSC to tick at the P0 ndfid rate */
+- msr = rdmsr(HWCR);
+- msr.lo |= 1 << 24;
+- wrmsr(HWCR, msr);
++ if (!is_fam15h()) {
++ /* Set TSC to tick at the P0 ndfid rate */
++ msr = rdmsr(HWCR);
++ msr.lo |= 1 << 24;
++ wrmsr(HWCR, msr);
++ }
+ }
+
+
+@@ -1012,8 +1052,7 @@ static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes)
+ /* Steps 1-6 of BIOS NB COF and VID Configuration
+ * for SVI and Single-Plane PVI Systems.
+ */
+-
+- fv.common_fid = init_fidvid_core(0,0);
++ fv.common_fid = init_fidvid_core(0, 0);
+
+ print_debug_fv("BSP fid = ", fv.common_fid);
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 8de6d25..aced850 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -30,9 +30,12 @@
+ #include <northbridge/amd/amdfam10/raminit_amdmct.c>
+ #include <reset.h>
+
++#if IS_ENABLED(CONFIG_SET_FIDVID)
+ static void prep_fid_change(void);
+ static void init_fidvid_stage2(u32 apicid, u32 nodeid);
+-void cpuSetAMDMSR(void);
++#endif
++
++void cpuSetAMDMSR(uint8_t node_id);
+
+ #if CONFIG_PCI_IO_CFG_EXT
+ static void set_EnableCf8ExtCfg(void)
+@@ -51,43 +54,38 @@ static void set_EnableCf8ExtCfg(void) { }
+
+ typedef void (*process_ap_t) (u32 apicid, void *gp);
+
+-//core_range = 0 : all cores
+-//core range = 1 : core 0 only
+-//core range = 2 : cores other than core0
++uint32_t get_boot_apic_id(uint8_t node, uint32_t core) {
++ uint32_t ap_apicid;
+
+-static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+- void *gp)
+-{
+- // here assume the OS don't change our apicid
+- u32 ap_apicid;
++ uint32_t nb_cfg_54;
++ uint32_t siblings;
++ uint32_t cores_found;
+
+- u32 nodes;
+- u32 siblings;
+- u32 disable_siblings;
+- u32 cores_found;
+- u32 nb_cfg_54;
+- int i, j;
+- u32 ApicIdCoreIdSize;
++ uint8_t fam15h = 0;
+ uint8_t rev_gte_d = 0;
+ uint8_t dual_node = 0;
+ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
+
+- /* get_nodes define in ht_wrapper.c */
+- nodes = get_nodes();
+-
+- if (!CONFIG_LOGICAL_CPUS ||
+- read_option(multi_core, 0) != 0) { // 0 means multi core
+- disable_siblings = 1;
+- } else {
+- disable_siblings = 0;
+- }
++ uint32_t ApicIdCoreIdSize;
+
+ /* Assume that all node are same stepping, otherwise we can use use
+ nb_cfg_54 from bsp for all nodes */
+ nb_cfg_54 = read_nb_cfg_54();
+ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
+
+- if (cpuid_eax(0x80000001) >= 0x8)
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f) {
++ /* Family 15h or later */
++ fam15h = 1;
++ nb_cfg_54 = 1;
++ }
++
++ if ((model >= 0x8) || fam15h)
+ /* Revision D or later */
+ rev_gte_d = 1;
+
+@@ -103,10 +101,63 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ siblings = 3; //quad core
+ }
+
++ cores_found = get_core_num_in_bsp(node);
++ if (siblings > cores_found)
++ siblings = cores_found;
++
++ if (dual_node) {
++ ap_apicid = 0;
++ if (fam15h) {
++ ap_apicid |= ((node >> 1) & 0x3) << 5; /* Node ID */
++ ap_apicid |= ((node & 0x1) * (siblings + 1)) + core; /* Core ID */
++ } else {
++ if (nb_cfg_54) {
++ ap_apicid |= ((node >> 1) & 0x3) << 4; /* Node ID */
++ ap_apicid |= ((node & 0x1) * (siblings + 1)) + core; /* Core ID */
++ } else {
++ ap_apicid |= node & 0x3; /* Node ID */
++ ap_apicid |= (((node & 0x1) * (siblings + 1)) + core) << 4; /* Core ID */
++ }
++ }
++ } else {
++ if (fam15h) {
++ ap_apicid = (node * (siblings + 1)) + core;
++ } else {
++ ap_apicid = node * (nb_cfg_54 ? (siblings + 1) : 1) +
++ core * (nb_cfg_54 ? 1 : 64);
++ }
++ }
++
++ return ap_apicid;
++}
++
++//core_range = 0 : all cores
++//core range = 1 : core 0 only
++//core range = 2 : cores other than core0
++
++static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
++ void *gp)
++{
++ // here assume the OS don't change our apicid
++ u32 ap_apicid;
++
++ u32 nodes;
++ u32 disable_siblings;
++ u32 cores_found;
++ int i, j;
++
++ /* get_nodes define in ht_wrapper.c */
++ nodes = get_nodes();
++
++ if (!CONFIG_LOGICAL_CPUS ||
++ read_option(multi_core, 0) != 0) { // 0 means multi core
++ disable_siblings = 1;
++ } else {
++ disable_siblings = 0;
++ }
++
+ for (i = 0; i < nodes; i++) {
+ cores_found = get_core_num_in_bsp(i);
+- if (siblings > cores_found)
+- siblings = cores_found;
+
+ u32 jstart, jend;
+
+@@ -123,21 +174,7 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ }
+
+ for (j = jstart; j <= jend; j++) {
+- if (dual_node) {
+- ap_apicid = 0;
+- if (nb_cfg_54) {
+- ap_apicid |= ((i >> 1) & 0x3) << 4; /* Node ID */
+- ap_apicid |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */
+- } else {
+- ap_apicid |= i & 0x3; /* Node ID */
+- ap_apicid |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */
+- }
+- } else {
+- ap_apicid =
+- i * (nb_cfg_54 ? (siblings + 1) : 1) +
+- j * (nb_cfg_54 ? 1 : 64);
+- }
+-
++ ap_apicid = get_boot_apic_id(i, j);
+
+ #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET > 0)
+ #if !CONFIG_LIFT_BSP_APIC_ID
+@@ -197,7 +234,7 @@ void print_apicid_nodeid_coreid(u32 apicid, struct node_core_id id,
+ apicid, id.nodeid, id.coreid);
+ }
+
+-static u32 wait_cpu_state(u32 apicid, u32 state)
++uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2)
+ {
+ u32 readback = 0;
+ u32 timeout = 1;
+@@ -205,7 +242,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state)
+ while (--loop > 0) {
+ if (lapic_remote_read(apicid, LAPIC_MSG_REG, &readback) != 0)
+ continue;
+- if ((readback & 0x3f) == state || (readback & 0x3f) == F10_APSTATE_RESET) {
++ if ((readback & 0x3f) == state || (readback & 0x3f) == state2 || (readback & 0x3f) == F10_APSTATE_RESET) {
+ timeout = 0;
+ break; //target cpu is in stage started
+ }
+@@ -222,7 +259,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state)
+ static void wait_ap_started(u32 ap_apicid, void *gp)
+ {
+ u32 timeout;
+- timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED);
++ timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED, F10_APSTATE_ASLEEP);
+ printk(BIOS_DEBUG, "* AP %02x", ap_apicid);
+ if (timeout) {
+ printk(BIOS_DEBUG, " timed out:%08x\n", timeout);
+@@ -258,16 +295,27 @@ static void enable_apic_ext_id(u32 node)
+ pci_write_config32(NODE_HT(node), 0x68, val);
+ }
+
+-static void STOP_CAR_AND_CPU(void)
++static void STOP_CAR_AND_CPU(uint8_t skip_sharedc_config, uint32_t apicid)
+ {
+ msr_t msr;
++ uint32_t family;
++
++ family = amd_fam1x_cpu_family(); // inline
++
++ if (family < 0x6f) {
++ /* Family 10h or earlier */
++
++ /* Disable L2 IC to L3 connection (Only for CAR) */
++ msr = rdmsr(BU_CFG2);
++ msr.lo &= ~(1 << ClLinesToNbDis);
++ wrmsr(BU_CFG2, msr);
++ }
+
+- /* Disable L2 IC to L3 connection (Only for CAR) */
+- msr = rdmsr(BU_CFG2);
+- msr.lo &= ~(1 << ClLinesToNbDis);
+- wrmsr(BU_CFG2, msr);
++ disable_cache_as_ram(skip_sharedc_config); // inline
++
++ /* Mark the core as sleeping */
++ lapic_write(LAPIC_MSG_REG, (apicid << 24) | F10_APSTATE_ASLEEP);
+
+- disable_cache_as_ram(); // inline
+ /* stop all cores except node0/core0 the bsp .... */
+ stop_this_cpu();
+ }
+@@ -276,6 +324,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ {
+ u32 bsp_apicid = 0;
+ u32 apicid;
++ uint8_t set_mtrrs;
+ struct node_core_id id;
+
+ /* Please refer to the calculations and explaination in cache_as_ram.inc before modifying these values */
+@@ -362,7 +411,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ */
+ update_microcode(cpuid_eax(1));
+
+- cpuSetAMDMSR();
++ cpuSetAMDMSR(id.nodeid);
+
+ #if CONFIG_SET_FIDVID
+ #if CONFIG_LOGICAL_CPUS && CONFIG_SET_FIDVID_CORE0_ONLY
+@@ -385,10 +434,29 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ }
+ #endif
+
++ if (is_fam15h()) {
++ /* core 1 on node 0 is special; to avoid corrupting the
++ * BSP do not alter MTRRs on that core */
++ if (apicid == 1)
++ set_mtrrs = 0;
++ else
++ set_mtrrs = !!(apicid & 0x1);
++ } else {
++ set_mtrrs = 1;
++ }
++
+ /* AP is ready, configure MTRRs and go to sleep */
+- set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK);
++ if (set_mtrrs)
++ set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK);
+
+- STOP_CAR_AND_CPU();
++ printk(BIOS_DEBUG, "Disabling CAR on AP %02x\n", apicid);
++ if (is_fam15h()) {
++ /* Only modify the MSRs on the odd cores (the last cores to finish booting) */
++ STOP_CAR_AND_CPU(!set_mtrrs, apicid);
++ } else {
++ /* Modify MSRs on all cores */
++ STOP_CAR_AND_CPU(0, apicid);
++ }
+
+ printk(BIOS_DEBUG,
+ "\nAP %02x should be halted but you are reading this....\n",
+@@ -496,7 +564,7 @@ static void setup_remote_node(u8 node)
+ }
+ #endif /* CONFIG_MAX_PHYSICAL_CPUS > 1 */
+
+-static void AMD_Errata281(u8 node, u32 revision, u32 platform)
++static void AMD_Errata281(u8 node, uint64_t revision, u32 platform)
+ {
+ /* Workaround for Transaction Scheduling Conflict in
+ * Northbridge Cross Bar. Implement XCS Token adjustment
+@@ -794,7 +862,7 @@ static void AMD_SetHtPhyRegister(u8 node, u8 link, u8 entry)
+ } while (!(val & HTPHY_IS_COMPLETE_MASK));
+ }
+
+-void cpuSetAMDMSR(void)
++void cpuSetAMDMSR(uint8_t node_id)
+ {
+ /* This routine loads the CPU with default settings in fam10_msr_default
+ * table . It must be run after Cache-As-RAM has been enabled, and
+@@ -804,7 +872,8 @@ void cpuSetAMDMSR(void)
+ */
+ msr_t msr;
+ u8 i;
+- u32 revision, platform;
++ u32 platform;
++ uint64_t revision;
+
+ printk(BIOS_DEBUG, "cpuSetAMDMSR ");
+
+@@ -824,6 +893,49 @@ void cpuSetAMDMSR(void)
+ }
+ AMD_Errata298();
+
++ if (revision & AMD_FAM15_ALL) {
++ uint32_t f5x80;
++ uint8_t enabled;
++ uint8_t compute_unit_count = 0;
++ f5x80 = pci_read_config32(NODE_PCI(node_id, 5), 0x80);
++ enabled = f5x80 & 0xf;
++ if (enabled == 0x1)
++ compute_unit_count = 1;
++ if (enabled == 0x3)
++ compute_unit_count = 2;
++ if (enabled == 0x7)
++ compute_unit_count = 3;
++ if (enabled == 0xf)
++ compute_unit_count = 4;
++ msr = rdmsr(BU_CFG2);
++ msr.lo &= ~(0x3 << 6); /* ThrottleNbInterface[1:0] */
++ msr.lo |= (((compute_unit_count - 1) & 0x3) << 6);
++ wrmsr(BU_CFG2, msr);
++ }
++
++ /* Revision C0 and above */
++ if (revision & AMD_OR_C0) {
++ uint32_t f3x1fc = pci_read_config32(NODE_PCI(node_id, 3), 0x1fc);
++ msr = rdmsr(FP_CFG);
++ msr.hi &= ~(0x7 << (42-32)); /* DiDtCfg4 */
++ msr.hi |= (((f3x1fc >> 17) & 0x7) << (42-32));
++ msr.hi &= ~(0x1 << (41-32)); /* DiDtCfg5 */
++ msr.hi |= (((f3x1fc >> 22) & 0x1) << (41-32));
++ msr.hi &= ~(0x1 << (40-32)); /* DiDtCfg3 */
++ msr.hi |= (((f3x1fc >> 16) & 0x1) << (40-32));
++ msr.hi &= ~(0x7 << (32-32)); /* DiDtCfg1 (1) */
++ msr.hi |= (((f3x1fc >> 11) & 0x7) << (32-32));
++ msr.lo &= ~(0x1f << 27); /* DiDtCfg1 (2) */
++ msr.lo |= (((f3x1fc >> 6) & 0x1f) << 27);
++ msr.lo &= ~(0x3 << 25); /* DiDtCfg2 */
++ msr.lo |= (((f3x1fc >> 14) & 0x3) << 25);
++ msr.lo &= ~(0x1f << 18); /* DiDtCfg0 */
++ msr.lo |= (((f3x1fc >> 1) & 0x1f) << 18);
++ msr.lo &= ~(0x1 << 16); /* DiDtMode */
++ msr.lo |= ((f3x1fc & 0x1) << 16);
++ wrmsr(FP_CFG, msr);
++ }
++
+ printk(BIOS_DEBUG, " done\n");
+ }
+
+@@ -835,9 +947,10 @@ static void cpuSetAMDPCI(u8 node)
+ * that it is run for the first core on each node
+ */
+ u8 i, j;
+- u32 revision, platform;
++ u32 platform;
+ u32 val;
+ u8 offset;
++ uint64_t revision;
+
+ printk(BIOS_DEBUG, "cpuSetAMDPCI %02d", node);
+
+@@ -899,6 +1012,7 @@ static void cpuSetAMDPCI(u8 node)
+ }
+
+ #ifdef UNUSED_CODE
++/* Clearing the MCA registers is apparently handled in the ramstage CPU Function 3 driver */
+ static void cpuInitializeMCA(void)
+ {
+ /* Clears Machine Check Architecture (MCA) registers, which power on
+diff --git a/src/cpu/amd/model_10xxx/model_10xxx_init.c b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+index b942c1a..8a61f13 100644
+--- a/src/cpu/amd/model_10xxx/model_10xxx_init.c
++++ b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+@@ -39,6 +39,23 @@
+
+ #define MCI_STATUS 0x401
+
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
++static volatile uint8_t fam15h_startup_flags[MAX_NODES_SUPPORTED][MAX_CORES_SUPPORTED] = {{ 0 }};
++
+ static void model_10xxx_init(device_t dev)
+ {
+ u8 i;
+@@ -47,13 +64,44 @@ static void model_10xxx_init(device_t dev)
+ #if CONFIG_LOGICAL_CPUS
+ u32 siblings;
+ #endif
++ uint8_t delay_start;
+
+ id = get_node_core_id(read_nb_cfg_54()); /* nb_cfg_54 can not be set */
+ printk(BIOS_DEBUG, "nodeid = %02d, coreid = %02d\n", id.nodeid, id.coreid);
+
++ if (is_fam15h())
++ delay_start = !!(id.coreid & 0x1);
++ else
++ delay_start = 0;
++
+ /* Turn on caching if we haven't already */
+ x86_enable_cache();
+- amd_setup_mtrrs();
++
++ if (!delay_start) {
++ /* Initialize all variable MTRRs except the first pair.
++ * This prevents Linux from having to correct an inconsistent
++ * MTRR setup, which would crash Family 15h CPUs due to the
++ * compute unit structure sharing MTRR MSRs between AP cores.
++ */
++ msr.hi = 0x00000000;
++ msr.lo = 0x00000000;
++
++ disable_cache();
++
++ for (i = 0x2; i < 0x10; i++) {
++ wrmsr(0x00000200 | i, msr);
++ }
++
++ enable_cache();
++
++ /* Set up other MTRRs */
++ amd_setup_mtrrs();
++ } else {
++ while (!fam15h_startup_flags[id.nodeid][id.coreid - 1]) {
++ /* Wait for CU first core startup */
++ }
++ }
++
+ x86_mtrr_check();
+
+ disable_cache();
+@@ -88,17 +136,24 @@ static void model_10xxx_init(device_t dev)
+ printk(BIOS_DEBUG, "siblings = %02d, ", siblings);
+ #endif
+
+- /* DisableCf8ExtCfg */
++ /* Disable Cf8ExtCfg */
+ msr = rdmsr(NB_CFG_MSR);
+ msr.hi &= ~(1 << (46 - 32));
+ wrmsr(NB_CFG_MSR, msr);
+
+- msr = rdmsr(BU_CFG2_MSR);
+- /* Clear ClLinesToNbDis */
+- msr.lo &= ~(1 << 15);
+- /* Clear bit 35 as per Erratum 343 */
+- msr.hi &= ~(1 << (35-32));
+- wrmsr(BU_CFG2_MSR, msr);
++ if (is_fam15h()) {
++ msr = rdmsr(BU_CFG3_MSR);
++ /* Set CombineCr0Cd */
++ msr.hi |= (1 << (49-32));
++ wrmsr(BU_CFG3_MSR, msr);
++ } else {
++ msr = rdmsr(BU_CFG2_MSR);
++ /* Clear ClLinesToNbDis */
++ msr.lo &= ~(1 << 15);
++ /* Clear bit 35 as per Erratum 343 */
++ msr.hi &= ~(1 << (35-32));
++ wrmsr(BU_CFG2_MSR, msr);
++ }
+
+ if (IS_ENABLED(CONFIG_HAVE_SMI_HANDLER)) {
+ printk(BIOS_DEBUG, "Initializing SMM ASeg memory\n");
+@@ -131,6 +186,7 @@ static void model_10xxx_init(device_t dev)
+ msr.lo |= (1 << 0);
+ wrmsr(HWCR_MSR, msr);
+
++ fam15h_startup_flags[id.nodeid][id.coreid] = 1;
+ }
+
+ static struct device_operations cpu_dev_ops = {
+@@ -147,15 +203,17 @@ static struct cpu_device_id cpu_table[] = {
+ { X86_VENDOR_AMD, 0x100f22 },
+ { X86_VENDOR_AMD, 0x100f23 },
+ { X86_VENDOR_AMD, 0x100f40 }, /* RB-C0 */
+- { X86_VENDOR_AMD, 0x100F42 }, /* RB-C2 */
+- { X86_VENDOR_AMD, 0x100F43 }, /* RB-C3 */
+- { X86_VENDOR_AMD, 0x100F52 }, /* BL-C2 */
+- { X86_VENDOR_AMD, 0x100F62 }, /* DA-C2 */
+- { X86_VENDOR_AMD, 0x100F63 }, /* DA-C3 */
+- { X86_VENDOR_AMD, 0x100F80 }, /* HY-D0 */
+- { X86_VENDOR_AMD, 0x100F81 }, /* HY-D1 */
+- { X86_VENDOR_AMD, 0x100F91 }, /* HY-D1 */
+- { X86_VENDOR_AMD, 0x100FA0 }, /* PH-E0 */
++ { X86_VENDOR_AMD, 0x100f42 }, /* RB-C2 */
++ { X86_VENDOR_AMD, 0x100f43 }, /* RB-C3 */
++ { X86_VENDOR_AMD, 0x100f52 }, /* BL-C2 */
++ { X86_VENDOR_AMD, 0x100f62 }, /* DA-C2 */
++ { X86_VENDOR_AMD, 0x100f63 }, /* DA-C3 */
++ { X86_VENDOR_AMD, 0x100f80 }, /* HY-D0 */
++ { X86_VENDOR_AMD, 0x100f81 }, /* HY-D1 */
++ { X86_VENDOR_AMD, 0x100f91 }, /* HY-D1 */
++ { X86_VENDOR_AMD, 0x100fa0 }, /* PH-E0 */
++ { X86_VENDOR_AMD, 0x600f12 }, /* OR-B2 */
++ { X86_VENDOR_AMD, 0x600f20 }, /* OR-C0 */
+ { 0, 0 },
+ };
+
+diff --git a/src/cpu/amd/model_10xxx/powernow_acpi.c b/src/cpu/amd/model_10xxx/powernow_acpi.c
+index 98ef08a..84e5514 100644
+--- a/src/cpu/amd/model_10xxx/powernow_acpi.c
++++ b/src/cpu/amd/model_10xxx/powernow_acpi.c
+@@ -74,8 +74,7 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p
+ /* Revision C or greater single-link processor */
+ cpuid1 = cpuid(0x80000008);
+ acpigen_write_PSD_package(0, (cpuid1.ecx & 0xff) + 1, SW_ALL);
+- }
+- else {
++ } else {
+ /* Find the local APIC ID for the specified core ID */
+ struct device* cpu;
+ int cpu_index = 0;
+@@ -99,7 +98,9 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p
+ }
+
+ /*
+-* For details of this algorithm, please refer to the BDKG 3.62 page 69
++* For details of this algorithm, please refer to:
++* Family 10h BDKG 3.62 page 69
++* Family 15h BDKG 3.14 page 74
+ *
+ * WARNING: The core count algorithm below assumes that all processors
+ * are identical, with the same number of active cores. While the BKDG
+@@ -149,6 +150,13 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ uint8_t node_count;
+ uint8_t cores_per_node;
+ uint8_t total_core_count;
++ uint8_t fam15h;
++ uint8_t fam10h_rev_e = 0;
++
++ /* Detect Revision E processors via method used in fidvid.c */
++ if ((cpuid_edx(0x80000007) & CPB_MASK)
++ && ((cpuid_ecx(0x80000008) & NC_MASK) == 5))
++ fam10h_rev_e = 1;
+
+ /*
+ * Based on the CPU socket type,cmp_cap and pwr_lmt , get the power limit.
+@@ -156,11 +164,17 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ * cmp_cap : 0x0 SingleCore ; 0x1 DualCore ; 0x2 TripleCore ; 0x3 QuadCore ; 0x4 QuintupleCore ; 0x5 HexCore
+ */
+ printk(BIOS_INFO, "Pstates algorithm ...\n");
++ fam15h = !!(mctGetLogicalCPUID(0) & AMD_FAM15_ALL);
+ /* Get number of cores */
+- dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xE8);
+- cmp_cap = (dtemp & 0x3000) >> 12;
+- if (mctGetLogicalCPUID(0) & AMD_FAM10_REV_D) /* revision D */
+- cmp_cap |= (dtemp & 0x8000) >> 13;
++ if (fam15h) {
++ cmp_cap = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 5)), 0x84) & 0xff;
++ } else {
++ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xe8);
++ cmp_cap = (dtemp & 0x3000) >> 12;
++ if (mctGetLogicalCPUID(0) & (AMD_FAM10_REV_D | AMD_FAM15_ALL)) /* revision D or higher */
++ cmp_cap |= (dtemp & 0x8000) >> 13;
++ }
++
+ /* Get number of nodes */
+ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 0)), 0x60);
+ node_count = ((dtemp & 0x70) >> 4) + 1;
+@@ -169,6 +183,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ /* Compute total number of cores installed in system */
+ total_core_count = cores_per_node * node_count;
+
++ /* Get number of boost states */
++ uint8_t boost_count = 0;
++ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 4)), 0x15c);
++ if (fam10h_rev_e)
++ boost_count = (dtemp >> 2) & 0x1;
++ else if (mctGetLogicalCPUID(0) & AMD_FAM15_ALL)
++ boost_count = (dtemp >> 2) & 0x7;
++
+ Pstate_num = 0;
+
+ /* See if the CPUID(0x80000007) returned EDX[7]==1b */
+@@ -205,7 +227,7 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+
+ /* Get PSmax's index */
+ msr = rdmsr(0xC0010061);
+- Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & BIT_MASK_3);
++ Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & ((fam15h)?BIT_MASK_7:BIT_MASK_3));
+
+ /* Determine if all enabled Pstates have the same fidvid */
+ uint8_t i;
+@@ -219,10 +241,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ }
+ }
+
++ /* Family 15h uses slightly different PSmax numbering */
++ if (fam15h)
++ Pstate_max++;
++
+ /* Populate tables with all Pstate information */
+ for (Pstate_num = 0; Pstate_num < Pstate_max; Pstate_num++) {
+ /* Get power state information */
+- msr = rdmsr(0xC0010064 + Pstate_num);
++ msr = rdmsr(0xC0010064 + Pstate_num + boost_count);
+ cpufid = (msr.lo & 0x3f);
+ cpudid = (msr.lo & 0x1c0) >> 6;
+ cpuvid = (msr.lo & 0xfe00) >> 9;
+@@ -232,12 +258,10 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ if (pviModeFlag) {
+ if (cpuvid >= 0x20) {
+ core_voltage = 7625 - (((cpuvid - 0x20) * 10000) / 80);
+- }
+- else {
++ } else {
+ core_voltage = 15500 - ((cpuvid * 10000) / 40);
+ }
+- }
+- else {
++ } else {
+ cpuvid = cpuvid & 0x7f;
+ if (cpuvid >= 0x7c)
+ core_voltage = 0;
+diff --git a/src/cpu/amd/model_10xxx/processor_name.c b/src/cpu/amd/model_10xxx/processor_name.c
+index 12c45c9..fbd0452 100644
+--- a/src/cpu/amd/model_10xxx/processor_name.c
++++ b/src/cpu/amd/model_10xxx/processor_name.c
+@@ -33,6 +33,10 @@
+ #include <cpu/amd/mtrr.h>
+ #include <cpu/cpu.h>
+ #include <cpu/amd/model_10xxx_rev.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pnp.h>
++#include <device/pci_ops.h>
+
+ /* The maximum length of CPU names is 48 bytes, including the final NULL byte.
+ * If you change these names your BIOS will _NOT_ pass the AMD validation and
+@@ -212,104 +216,138 @@ static int strcpymax(char *dst, const char *src, int buflen)
+ return i;
+ }
+
++#define NAME_STRING_MAXLEN 48
+
+ int init_processor_name(void)
+ {
+- /* variable names taken from fam10 revision guide for clarity */
+- u32 BrandId; /* CPUID Fn8000_0001_EBX */
+- u8 String1; /* BrandID[14:11] */
+- u8 String2; /* BrandID[3:0] */
+- u8 Model; /* BrandID[10:4] */
+- u8 Pg; /* BrandID[15] */
+- u8 PkgTyp; /* BrandID[31:28] */
+- u8 NC; /* CPUID Fn8000_0008_ECX */
+- const char *processor_name_string = unknown;
+- char program_string[48];
+- u32 *p_program_string = (u32 *)program_string;
+ msr_t msr;
+- int i, j = 0, str2_checkNC = 1;
+- const struct str_s *str, *str2;
++ ssize_t i;
++ char program_string[NAME_STRING_MAXLEN];
++ u32 *p_program_string = (u32 *)program_string;
++ uint8_t fam15h = 0;
++ uint32_t family;
+
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+- /* Find out which CPU brand it is */
+- BrandId = cpuid_ebx(0x80000001);
+- String1 = (u8)((BrandId >> 11) & 0x0F);
+- String2 = (u8)((BrandId >> 0) & 0x0F);
+- Model = (u8)((BrandId >> 4) & 0x7F);
+- Pg = (u8)((BrandId >> 15) & 0x01);
+- PkgTyp = (u8)((BrandId >> 28) & 0x0F);
+- NC = (u8)(cpuid_ecx(0x80000008) & 0xFF);
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
+
+ /* null the string */
+ memset(program_string, 0, sizeof(program_string));
+
+- if (!Model) {
+- processor_name_string = Pg ? thermal : sample;
+- goto done;
+- }
+-
+- switch (PkgTyp) {
+- case 0: /* F1207 */
+- str = String1_socket_F;
+- str2 = String2_socket_F;
+- str2_checkNC = 0;
+- break;
+- case 1: /* AM2 */
+- str = String1_socket_AM2;
+- str2 = String2_socket_AM2;
+- break;
+- case 3: /* G34 */
+- str = String1_socket_G34;
+- str2 = String2_socket_G34;
+- str2_checkNC = 0;
+- break;
+- case 5: /* C32 */
+- str = String1_socket_C32;
+- str2 = String2_socket_C32;
+- break;
+- default:
+- goto done;
+- }
++ if (fam15h) {
++ /* Family 15h or later */
++ uint32_t dword;
++ device_t cpu_fn5_dev = dev_find_slot(0, PCI_DEVFN(0x18, 5));
++ pci_write_config32(cpu_fn5_dev, 0x194, 0);
++ dword = pci_read_config32(cpu_fn5_dev, 0x198);
++ if (dword == 0) {
++ strcpymax(program_string, sample, sizeof(program_string));
++ } else {
++ /* Assemble the string from PCI configuration register contents */
++ for (i = 0; i < 12; i++) {
++ pci_write_config32(cpu_fn5_dev, 0x194, i);
++ p_program_string[i] = pci_read_config32(cpu_fn5_dev, 0x198);
++ }
++
++ /* Correctly place the null terminator */
++ for (i = (NAME_STRING_MAXLEN - 2); i > 0; i--) {
++ if (program_string[i] != 0x20)
++ break;
++ }
++ program_string[i + 1] = 0;
++ }
++ } else {
++ /* variable names taken from fam10 revision guide for clarity */
++ u32 BrandId; /* CPUID Fn8000_0001_EBX */
++ u8 String1; /* BrandID[14:11] */
++ u8 String2; /* BrandID[3:0] */
++ u8 Model; /* BrandID[10:4] */
++ u8 Pg; /* BrandID[15] */
++ u8 PkgTyp; /* BrandID[31:28] */
++ u8 NC; /* CPUID Fn8000_0008_ECX */
++ const char *processor_name_string = unknown;
++ int j = 0, str2_checkNC = 1;
++ const struct str_s *str, *str2;
++
++ /* Find out which CPU brand it is */
++ BrandId = cpuid_ebx(0x80000001);
++ String1 = (u8)((BrandId >> 11) & 0x0F);
++ String2 = (u8)((BrandId >> 0) & 0x0F);
++ Model = (u8)((BrandId >> 4) & 0x7F);
++ Pg = (u8)((BrandId >> 15) & 0x01);
++ PkgTyp = (u8)((BrandId >> 28) & 0x0F);
++ NC = (u8)(cpuid_ecx(0x80000008) & 0xFF);
++
++ if (!Model) {
++ processor_name_string = Pg ? thermal : sample;
++ goto done;
++ }
+
+- /* String1 */
+- for (i = 0; str[i].value; i++) {
+- if ((str[i].Pg == Pg) &&
+- (str[i].NC == NC) &&
+- (str[i].String == String1)) {
+- processor_name_string = str[i].value;
++ switch (PkgTyp) {
++ case 0: /* F1207 */
++ str = String1_socket_F;
++ str2 = String2_socket_F;
++ str2_checkNC = 0;
++ break;
++ case 1: /* AM2 */
++ str = String1_socket_AM2;
++ str2 = String2_socket_AM2;
++ break;
++ case 3: /* G34 */
++ str = String1_socket_G34;
++ str2 = String2_socket_G34;
++ str2_checkNC = 0;
++ break;
++ case 5: /* C32 */
++ str = String1_socket_C32;
++ str2 = String2_socket_C32;
+ break;
++ default:
++ goto done;
+ }
+- }
+
+- if (!str[i].value)
+- goto done;
++ /* String1 */
++ for (i = 0; str[i].value; i++) {
++ if ((str[i].Pg == Pg) &&
++ (str[i].NC == NC) &&
++ (str[i].String == String1)) {
++ processor_name_string = str[i].value;
++ break;
++ }
++ }
+
+- j = strcpymax(program_string, processor_name_string,
+- sizeof(program_string));
++ if (!str[i].value)
++ goto done;
+
+- /* Translate Model from 01-99 to ASCII and put it on the end.
+- * Numbers less than 10 should include a leading zero, e.g., 09.*/
+- if (Model < 100 && j < sizeof(program_string) - 2) {
+- program_string[j++] = (Model / 10) + '0';
+- program_string[j++] = (Model % 10) + '0';
+- }
++ j = strcpymax(program_string, processor_name_string,
++ sizeof(program_string));
+
+- processor_name_string = unknown2;
+-
+- /* String 2 */
+- for(i = 0; str2[i].value; i++) {
+- if ((str2[i].Pg == Pg) &&
+- ((str2[i].NC == NC) || !str2_checkNC) &&
+- (str2[i].String == String2)) {
+- processor_name_string = str2[i].value;
+- break;
++ /* Translate Model from 01-99 to ASCII and put it on the end.
++ * Numbers less than 10 should include a leading zero, e.g., 09.*/
++ if (Model < 100 && j < sizeof(program_string) - 2) {
++ program_string[j++] = (Model / 10) + '0';
++ program_string[j++] = (Model % 10) + '0';
+ }
+- }
+
++ processor_name_string = unknown2;
++
++ /* String 2 */
++ for(i = 0; str2[i].value; i++) {
++ if ((str2[i].Pg == Pg) &&
++ ((str2[i].NC == NC) || !str2_checkNC) &&
++ (str2[i].String == String2)) {
++ processor_name_string = str2[i].value;
++ break;
++ }
++ }
+
+-done:
+- strcpymax(&program_string[j], processor_name_string,
+- sizeof(program_string) - j);
++ done:
++ strcpymax(&program_string[j], processor_name_string,
++ sizeof(program_string) - j);
++ }
+
+ printk(BIOS_DEBUG, "CPU model: %s\n", program_string);
+
+diff --git a/src/cpu/amd/model_10xxx/update_microcode.c b/src/cpu/amd/model_10xxx/update_microcode.c
+index 51aca35..3b2f5dd 100644
+--- a/src/cpu/amd/model_10xxx/update_microcode.c
++++ b/src/cpu/amd/model_10xxx/update_microcode.c
+@@ -28,6 +28,7 @@ struct id_mapping {
+
+ static u16 get_equivalent_processor_rev_id(u32 orig_id) {
+ static const struct id_mapping id_mapping_table[] = {
++ /* Family 10h */
+ { 0x100f00, 0x1000 },
+ { 0x100f01, 0x1000 },
+ { 0x100f02, 0x1000 },
+@@ -42,8 +43,13 @@ static u16 get_equivalent_processor_rev_id(u32 orig_id) {
+ { 0x100f62, 0x1062 }, /* DA-C2 */
+ { 0x100f63, 0x1043 }, /* DA-C3 */
+ { 0x100f81, 0x1081 }, /* HY-D1 */
++ { 0x100f91, 0x1081 }, /* HY-D1 */
+ { 0x100fa0, 0x10A0 }, /* PH-E0 */
+
++ /* Family 15h */
++ { 0x600f12, 0x6012 }, /* OR-B2 */
++ { 0x600f20, 0x6020 }, /* OR-C0 */
++
+ /* Array terminator */
+ { 0xffffff, 0x0000 },
+ };
+diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c
+index 9c21e94..8a9b5ed 100644
+--- a/src/cpu/amd/quadcore/quadcore.c
++++ b/src/cpu/amd/quadcore/quadcore.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -26,16 +27,41 @@
+
+ #include "cpu/amd/quadcore/quadcore_id.c"
+
++/* get_boot_apic_id and wait_cpu_state located in init_cpus.c */
++uint32_t get_boot_apic_id(uint8_t node, uint32_t core);
++uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2);
++
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
+ static u32 get_core_num_in_bsp(u32 nodeid)
+ {
+ u32 dword;
+- dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8);
+- dword >>= 12;
+- /* Bit 15 is CmpCap[2] since Revision D. */
+- if ((cpuid_ecx(0x80000008) & 0xff) > 3)
+- dword = ((dword & 8) >> 1) | (dword & 3);
+- else
+- dword &= 3;
++ if (is_fam15h()) {
++ /* Family 15h moved CmpCap to F5x84 [7:0] */
++ dword = pci_read_config32(NODE_PCI(nodeid, 5), 0x84);
++ dword &= 0xff;
++ } else {
++ dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8);
++ dword >>= 12;
++ /* Bit 15 is CmpCap[2] since Revision D. */
++ if ((cpuid_ecx(0x80000008) & 0xff) > 3)
++ dword = ((dword & 8) >> 1) | (dword & 3);
++ else
++ dword &= 3;
++ }
+ return dword;
+ }
+
+@@ -50,28 +76,68 @@ static u8 set_apicid_cpuid_lo(void)
+ return 1;
+ }
+
+-static void real_start_other_core(u32 nodeid, u32 cores)
++static void real_start_other_core(uint32_t nodeid, uint32_t cores)
+ {
+- u32 dword, i;
++ ssize_t i;
++ uint32_t dword;
+
+ printk(BIOS_DEBUG, "Start other core - nodeid: %02x cores: %02x\n", nodeid, cores);
+
+ /* set PCI_DEV(0, 0x18+nodeid, 3), 0x44 bit 27 to redirect all MC4
+ accesses and error logging to core0 */
+ dword = pci_read_config32(NODE_PCI(nodeid, 3), 0x44);
+- dword |= 1 << 27; // NbMcaToMstCpuEn bit
++ dword |= 1 << 30; /* SyncFloodOnDramAdrParErr=1 */
++ dword |= 1 << 27; /* NbMcaToMstCpuEn=1 */
++ dword |= 1 << 21; /* SyncFloodOnAnyUcErr=1 */
++ dword |= 1 << 20; /* SyncFloodOnWDT=1 */
++ dword |= 1 << 2; /* SyncFloodOnDramUcEcc=1 */
+ pci_write_config32(NODE_PCI(nodeid, 3), 0x44, dword);
+- // set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1
+- dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68);
+- dword |= 1 << 5;
+- pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword);
+-
+- if(cores > 1) {
+- dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168);
+- for (i = 0; i < cores - 1; i++) {
+- dword |= 1 << i;
++ if (is_fam15h()) {
++ uint32_t core_activation_flags = 0;
++ uint32_t active_cores = 0;
++
++ /* Set PCI_DEV(0, 0x18+nodeid, 0), 0x1dc bits 7:1 to start cores */
++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x1dc);
++ for (i = 1; i < cores + 1; i++) {
++ core_activation_flags |= 1 << i;
++ }
++
++ /* Start the first core of each compute unit */
++ active_cores |= core_activation_flags & 0x55;
++ pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores);
++
++ /* Each core shares a single set of MTRR registers with
++ * another core in the same compute unit, therefore, it
++ * is important that one core in each CU starts in advance
++ * of the other in order to avoid one core stomping all over
++ * the other core's settings.
++ */
++
++ /* Wait for the first core of each compute unit to start... */
++ uint32_t timeout;
++ for (i = 1; i < cores + 1; i++) {
++ if (!(i & 0x1)) {
++ uint32_t ap_apicid = get_boot_apic_id(nodeid, i);
++ timeout = wait_cpu_state(ap_apicid, F10_APSTATE_ASLEEP, F10_APSTATE_ASLEEP);
++ }
++ }
++
++ /* Start the second core of each compute unit */
++ active_cores |= core_activation_flags & 0xaa;
++ pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores);
++ } else {
++ // set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1
++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68);
++ dword |= 1 << 5;
++ pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword);
++
++ if (cores > 1) {
++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168);
++ for (i = 0; i < cores - 1; i++) {
++ dword |= 1 << i;
++ }
++ pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword);
+ }
+- pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword);
+ }
+ }
+
+@@ -91,10 +157,9 @@ static void start_other_cores(void)
+
+ for (nodeid = 0; nodeid < nodes; nodeid++) {
+ u32 cores = get_core_num_in_bsp(nodeid);
+- printk(BIOS_DEBUG, "init node: %02x cores: %02x \n", nodeid, cores);
++ printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1 \n", nodeid, cores);
+ if (cores > 0) {
+ real_start_other_core(nodeid, cores);
+ }
+ }
+-
+ }
+diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c
+index 778e96f..c0537b3 100644
+--- a/src/cpu/amd/quadcore/quadcore_id.c
++++ b/src/cpu/amd/quadcore/quadcore_id.c
+@@ -43,9 +43,12 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ {
+ struct node_core_id id;
+ uint8_t apicid;
++ uint8_t fam15h = 0;
+ uint8_t rev_gte_d = 0;
+ uint8_t dual_node = 0;
+ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
+
+ #ifdef __PRE_RAM__
+ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
+@@ -53,7 +56,17 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
+ #endif
+
+- if (cpuid_eax(0x80000001) >= 0x8)
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f) {
++ /* Family 15h or later */
++ fam15h = 1;
++ nb_cfg_54 = 1;
++ }
++
++ if ((model >= 0x8) || fam15h)
+ /* Revision D or later */
+ rev_gte_d = 1;
+
+@@ -67,7 +80,13 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ */
+ apicid = (cpuid_ebx(1) >> 24) & 0xff;
+ if( nb_cfg_54) {
+- if (rev_gte_d && dual_node) {
++ if (fam15h && dual_node) {
++ id.coreid = apicid & 0x1f;
++ id.nodeid = (apicid & 0x60) >> 5;
++ } else if (fam15h && !dual_node) {
++ id.coreid = apicid & 0xf;
++ id.nodeid = (apicid & 0x70) >> 4;
++ } else if (rev_gte_d && dual_node) {
+ id.coreid = apicid & 0xf;
+ id.nodeid = (apicid & 0x30) >> 4;
+ } else if (rev_gte_d && !dual_node) {
+@@ -90,7 +109,25 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ }
+ }
+
+- if (rev_gte_d) {
++ if (fam15h && dual_node) {
++ /* Coreboot expects each separate processor die to be on a different nodeid.
++ * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
++ */
++ uint32_t f5x84;
++ uint8_t core_count;
++
++#ifdef __PRE_RAM__
++ f5x84 = pci_read_config32(NODE_PCI(0, 5), 0x84);
++#else
++ f5x84 = pci_read_config32(get_node_pci(0, 5), 0x84);
++#endif
++ core_count = (f5x84 & 0xff) + 1;
++ id.nodeid = id.nodeid * 2;
++ if (id.coreid >= core_count) {
++ id.nodeid += 1;
++ id.coreid = id.coreid - core_count;
++ }
++ } else if (rev_gte_d && dual_node) {
+ /* Coreboot expects each separate processor die to be on a different nodeid.
+ * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
+ */
+diff --git a/src/include/cpu/amd/model_10xxx_msr.h b/src/include/cpu/amd/model_10xxx_msr.h
+index 6c7dece..7d78e2d 100644
+--- a/src/include/cpu/amd/model_10xxx_msr.h
++++ b/src/include/cpu/amd/model_10xxx_msr.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -32,7 +33,13 @@
+ #define IC_CFG_MSR 0xC0011021
+ #define DC_CFG_MSR 0xC0011022
+ #define BU_CFG_MSR 0xC0011023
++#define FP_CFG_MSR 0xC0011028
++#define DE_CFG_MSR 0xC0011029
+ #define BU_CFG2_MSR 0xC001102A
++#define BU_CFG3_MSR 0xC001102B
++#define EX_CFG_MSR 0xC001102C
++#define LS_CFG2_MSR 0xC001102D
++#define IBS_OP_DATA3_MSR 0xC0011037
+
+ #define CPU_ID_FEATURES_MSR 0xC0011004
+ #define CPU_ID_HYPER_EXT_FEATURES 0xC001100d
+diff --git a/src/northbridge/amd/amdfam10/Kconfig b/src/northbridge/amd/amdfam10/Kconfig
+index ff92fca..b84eabf 100644
+--- a/src/northbridge/amd/amdfam10/Kconfig
++++ b/src/northbridge/amd/amdfam10/Kconfig
+@@ -92,7 +92,7 @@ endif
+ if HAVE_ACPI_RESUME
+ config S3_DATA_SIZE
+ int
+- default 16384
++ default 32768
+ endif
+
+ if DIMM_DDR2
+diff --git a/src/northbridge/amd/amdfam10/Makefile.inc b/src/northbridge/amd/amdfam10/Makefile.inc
+index b4097b4..4098dce 100644
+--- a/src/northbridge/amd/amdfam10/Makefile.inc
++++ b/src/northbridge/amd/amdfam10/Makefile.inc
+@@ -2,6 +2,8 @@ ifeq ($(CONFIG_NORTHBRIDGE_AMD_AMDFAM10),y)
+
+ ramstage-y += northbridge.c
+ ramstage-y += misc_control.c
++ramstage-y += link_control.c
++ramstage-y += nb_control.c
+ romstage-y += amdfam10_util.c
+ ramstage-y += amdfam10_util.c
+
+diff --git a/src/northbridge/amd/amdfam10/amdfam10.h b/src/northbridge/amd/amdfam10/amdfam10.h
+index a1e08a0..b724394 100644
+--- a/src/northbridge/amd/amdfam10/amdfam10.h
++++ b/src/northbridge/amd/amdfam10/amdfam10.h
+@@ -962,9 +962,12 @@ that are corresponding to 0x01, 0x02, 0x03, 0x05, 0x06, 0x07
+
+ #define LAPIC_MSG_REG 0x380
+ #define F10_APSTATE_STARTED 0x13 // start of AP execution
+-#define F10_APSTATE_STOPPED 0x14 // allow AP to stop
++#define F10_APSTATE_ASLEEP 0x14 // AP sleeping
++#define F10_APSTATE_STOPPED 0x15 // allow AP to stop
+ #define F10_APSTATE_RESET 0x01 // waiting for warm reset
+
++#define MAX_CORES_SUPPORTED 128
++
+ #include "nums.h"
+
+ #ifdef __PRE_RAM__
+@@ -1038,7 +1041,6 @@ struct sys_info {
+
+ struct MCTStatStruc MCTstat;
+ struct DCTStatStruc DCTstatA[NODE_NUMS];
+-
+ } __attribute__((packed));
+
+ #ifdef __PRE_RAM__
+diff --git a/src/northbridge/amd/amdfam10/amdfam10_util.c b/src/northbridge/amd/amdfam10/amdfam10_util.c
+index 423bb73..a4045bdf 100644
+--- a/src/northbridge/amd/amdfam10/amdfam10_util.c
++++ b/src/northbridge/amd/amdfam10/amdfam10_util.c
+@@ -34,14 +34,14 @@ u32 Get_NB32(u32 dev, u32 reg)
+ }
+ #endif
+
+-u32 mctGetLogicalCPUID(u32 Node)
++uint64_t mctGetLogicalCPUID(u32 Node)
+ {
+ /* Converts the CPUID to a logical ID MASK that is used to check
+ CPU version support versions */
+ u32 dev;
+ u32 val, valx;
+ u32 family, model, stepping;
+- u32 ret;
++ uint64_t ret;
+
+ if (Node == 0xFF) { /* current node */
+ val = cpuid_eax(0x80000001);
+@@ -100,9 +100,16 @@ u32 mctGetLogicalCPUID(u32 Node)
+ case 0x100a0:
+ ret = AMD_PH_E0;
+ break;
++ case 0x15012:
++ case 0x1501f:
++ ret = AMD_OR_B2;
++ break;
++ case 0x15020:
++ ret = AMD_OR_C0;
++ break;
+ default:
+ /* FIXME: mabe we should die() here. */
+- printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! \n");
++ printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! %08x\n", valx);
+ ret = 0;
+ }
+
+diff --git a/src/northbridge/amd/amdfam10/link_control.c b/src/northbridge/amd/amdfam10/link_control.c
+new file mode 100644
+index 0000000..1091ef4
+--- /dev/null
++++ b/src/northbridge/amd/amdfam10/link_control.c
+@@ -0,0 +1,86 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/* Configure various power control registers, including processor
++ * boost support.
++ */
++
++#include <console/console.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <device/pci_ops.h>
++#include <pc80/mc146818rtc.h>
++#include <lib.h>
++#include <cpu/amd/model_10xxx_rev.h>
++
++#include "amdfam10.h"
++
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
++static void nb_control_init(struct device *dev)
++{
++ uint32_t dword;
++
++ printk(BIOS_DEBUG, "NB: Function 4 Link Control.. ");
++
++ if (is_fam15h()) {
++ /* Enable APM */
++ dword = pci_read_config32(dev, 0x15c);
++ dword |= (0x1 << 7); /* ApmMasterEn = 1 */
++ pci_write_config32(dev, 0x15c, dword);
++ }
++
++ printk(BIOS_DEBUG, "done.\n");
++}
++
++
++static struct device_operations mcf4_ops = {
++ .read_resources = pci_dev_read_resources,
++ .set_resources = pci_dev_set_resources,
++ .enable_resources = pci_dev_enable_resources,
++ .init = nb_control_init,
++ .scan_bus = 0,
++ .ops_pci = 0,
++};
++
++static const struct pci_driver mcf4_driver_fam10 __pci_driver = {
++ .ops = &mcf4_ops,
++ .vendor = PCI_VENDOR_ID_AMD,
++ .device = 0x1204,
++};
++
++static const struct pci_driver mcf4_driver_fam15 __pci_driver = {
++ .ops = &mcf4_ops,
++ .vendor = PCI_VENDOR_ID_AMD,
++ .device = 0x1604,
++};
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index 85c8838..8777e8f 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -153,3 +153,9 @@ static const struct pci_driver mcf3_driver __pci_driver = {
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = 0x1203,
+ };
++
++static const struct pci_driver mcf3_driver_fam15 __pci_driver = {
++ .ops = &mcf3_ops,
++ .vendor = PCI_VENDOR_ID_AMD,
++ .device = 0x1603,
++};
+diff --git a/src/northbridge/amd/amdfam10/nb_control.c b/src/northbridge/amd/amdfam10/nb_control.c
+new file mode 100644
+index 0000000..f95b6f8
+--- /dev/null
++++ b/src/northbridge/amd/amdfam10/nb_control.c
+@@ -0,0 +1,85 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/* Configure various power control registers, including processor boost
++ * and TDP monitoring support.
++ */
++
++#include <console/console.h>
++#include <device/device.h>
++#include <device/pci.h>
++#include <device/pci_ids.h>
++#include <device/pci_ops.h>
++#include <pc80/mc146818rtc.h>
++#include <lib.h>
++#include <cpu/amd/model_10xxx_rev.h>
++
++#include "amdfam10.h"
++
++static void nb_control_init(struct device *dev)
++{
++ uint32_t dword;
++ uint32_t f5x80;
++ uint8_t cu_enabled;
++ uint8_t compute_unit_count = 0;
++
++ printk(BIOS_DEBUG, "NB: Function 5 Northbridge Control.. ");
++
++ /* Determine the number of active compute units on this node */
++ f5x80 = pci_read_config32(dev, 0x80);
++ cu_enabled = f5x80 & 0xf;
++ if (cu_enabled == 0x1)
++ compute_unit_count = 1;
++ if (cu_enabled == 0x3)
++ compute_unit_count = 2;
++ if (cu_enabled == 0x7)
++ compute_unit_count = 3;
++ if (cu_enabled == 0xf)
++ compute_unit_count = 4;
++
++ /* Configure Processor TDP Running Average */
++ dword = pci_read_config32(dev, 0xe0);
++ dword &= ~0xf; /* RunAvgRange = 0x9 */
++ dword |= 0x9;
++ pci_write_config32(dev, 0xe0, dword);
++
++ /* Configure northbridge P-states */
++ dword = pci_read_config32(dev, 0xe0);
++ dword &= ~(0x7 << 9); /* NbPstateThreshold = compute_unit_count */
++ dword |= (compute_unit_count & 0x7) << 9;
++ pci_write_config32(dev, 0xe0, dword);
++
++ printk(BIOS_DEBUG, "done.\n");
++}
++
++
++static struct device_operations mcf5_ops = {
++ .read_resources = pci_dev_read_resources,
++ .set_resources = pci_dev_set_resources,
++ .enable_resources = pci_dev_enable_resources,
++ .init = nb_control_init,
++ .scan_bus = 0,
++ .ops_pci = 0,
++};
++
++static const struct pci_driver mcf5_driver_fam15 __pci_driver = {
++ .ops = &mcf5_ops,
++ .vendor = PCI_VENDOR_ID_AMD,
++ .device = 0x1605,
++};
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index d0a0787..a29dad9 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -79,6 +79,21 @@ device_t get_node_pci(u32 nodeid, u32 fn)
+ #endif
+ }
+
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
+ static void get_fx_devs(void)
+ {
+ int i;
+@@ -195,24 +210,33 @@ static void amd_g34_fixup(struct bus *link, device_t dev)
+ uint8_t rev_gte_d = 0;
+ uint8_t dual_node = 0;
+ uint32_t f3xe8;
+-
++
+ if (cpuid_eax(0x80000001) >= 0x8)
+ /* Revision D or later */
+ rev_gte_d = 1;
+-
+- if (rev_gte_d) {
++
++ if (rev_gte_d || is_fam15h()) {
+ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
+-
++
+ /* Check for dual node capability */
+ if (f3xe8 & 0x20000000)
+ dual_node = 1;
+-
++
+ if (dual_node) {
+ /* Each G34 processor contains a defective HT link.
+ * See the BKDG Rev 3.62 section 2.7.1.5 for details.
+ */
+ f3xe8 = pci_read_config32(get_node_pci(nodeid, 3), 0xe8);
+ uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
++ uint8_t defective_link_number_1;
++ uint8_t defective_link_number_2;
++ if (is_fam15h()) {
++ defective_link_number_1 = 4; /* Link 0 Sublink 1 */
++ defective_link_number_2 = 7; /* Link 3 Sublink 1 */
++ } else {
++ defective_link_number_1 = 6; /* Link 2 Sublink 1 */
++ defective_link_number_2 = 5; /* Link 1 Sublink 1 */
++ }
+ if (internal_node_number == 0) {
+ /* Node 0 */
+ if (link->link_num == 6) /* Link 2 Sublink 1 */
+@@ -312,6 +336,46 @@ static void amdfam10_scan_chains(device_t dev)
+ {
+ struct bus *link;
+
++#if CONFIG_CPU_AMD_SOCKET_G34_NON_AGESA
++ if (is_fam15h()) {
++ uint8_t current_link_number = 0;
++
++ for (link = dev->link_list; link; link = link->next) {
++ /* The following links have changed position in Fam15h G34 processors:
++ * Fam10 Fam15
++ * Node 0
++ * L3 --> L1
++ * L0 --> L3
++ * L1 --> L2
++ * L2 --> L0
++ * Node 1
++ * L0 --> L0
++ * L1 --> L3
++ * L2 --> L1
++ * L3 --> L2
++ */
++ if (link->link_num == 0)
++ link->link_num = 3;
++ else if (link->link_num == 1)
++ link->link_num = 2;
++ else if (link->link_num == 2)
++ link->link_num = 0;
++ else if (link->link_num == 3)
++ link->link_num = 1;
++ else if (link->link_num == 5)
++ link->link_num = 7;
++ else if (link->link_num == 6)
++ link->link_num = 5;
++ else if (link->link_num == 7)
++ link->link_num = 6;
++
++ current_link_number++;
++ if (current_link_number > 3)
++ current_link_number = 0;
++ }
++ }
++#endif
++
+ /* Do sb ht chain at first, in case s2885 put sb chain (8131/8111) on link2, but put 8151 on link0 */
+ trim_ht_chain(dev);
+
+@@ -618,13 +682,21 @@ static const struct pci_driver mcf0_driver __pci_driver = {
+ .device = 0x1200,
+ };
+
++
+ static void amdfam10_nb_init(void *chip_info)
+ {
+ relocate_sb_ht_chain();
+ }
+
++static const struct pci_driver mcf0_driver_fam15 __pci_driver = {
++ .ops = &northbridge_operations,
++ .vendor = PCI_VENDOR_ID_AMD,
++ .device = 0x1600,
++};
++
++
+ struct chip_operations northbridge_amd_amdfam10_ops = {
+- CHIP_NAME("AMD FAM10 Northbridge")
++ CHIP_NAME("AMD Family 10h/15h Northbridge")
+ .enable_dev = 0,
+ .init = amdfam10_nb_init,
+ };
+@@ -948,38 +1020,61 @@ static int amdfam10_get_smbios_data16(int* count, int handle, unsigned long *cur
+
+ static uint16_t amdmct_mct_speed_enum_to_mhz(uint8_t speed)
+ {
+- if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
+- switch (speed) {
+- case 1:
+- return 200;
+- case 2:
+- return 266;
+- case 3:
+- return 333;
+- case 4:
+- return 400;
+- case 5:
+- return 533;
+- default:
+- return 0;
+- }
+- } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+- switch (speed) {
+- case 3:
+- return 333;
+- case 4:
+- return 400;
+- case 5:
+- return 533;
+- case 6:
+- return 667;
+- case 7:
+- return 800;
+- default:
+- return 0;
++ if (is_fam15h()) {
++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ switch (speed) {
++ case 0x4:
++ return 333;
++ case 0x6:
++ return 400;
++ case 0xa:
++ return 533;
++ case 0xe:
++ return 667;
++ case 0x12:
++ return 800;
++ case 0x16:
++ return 933;
++ default:
++ return 0;
++ }
++ } else {
++ return 0;
+ }
+ } else {
+- return 0;
++ if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
++ switch (speed) {
++ case 1:
++ return 200;
++ case 2:
++ return 266;
++ case 3:
++ return 333;
++ case 4:
++ return 400;
++ case 5:
++ return 533;
++ default:
++ return 0;
++ }
++ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ switch (speed) {
++ case 3:
++ return 333;
++ case 4:
++ return 400;
++ case 5:
++ return 533;
++ case 6:
++ return 667;
++ case 7:
++ return 800;
++ default:
++ return 0;
++ }
++ } else {
++ return 0;
++ }
+ }
+ }
+
+@@ -1073,6 +1168,8 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+ /* Find the maximum and minimum supported voltages */
+ uint8_t supported_voltages = mem_info->dct_stat[node].DimmSupportedVoltages[slot];
++ uint8_t configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot];
++
+ if (supported_voltages & 0x8)
+ t->minimum_voltage = 1150;
+ else if (supported_voltages & 0x4)
+@@ -1091,7 +1188,14 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ else if (supported_voltages & 0x8)
+ t->maximum_voltage = 1150;
+
+- t->configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot];
++ if (configured_voltage & 0x8)
++ t->configured_voltage = 1150;
++ else if (configured_voltage & 0x4)
++ t->configured_voltage = 1250;
++ else if (configured_voltage & 0x2)
++ t->configured_voltage = 1350;
++ else if (configured_voltage & 0x1)
++ t->configured_voltage = 1500;
+ }
+ t->memory_error_information_handle = 0xFFFE; /* no error information handle available */
+ single_len = t->length + smbios_string_table_len(t->eos);
+@@ -1229,12 +1333,14 @@ static void cpu_bus_scan(device_t dev)
+ #if CONFIG_CBB
+ device_t pci_domain;
+ #endif
++ int nvram = 0;
+ int i,j;
+ int nodes;
+ unsigned nb_cfg_54;
+ unsigned siblings;
+ int cores_found;
+ int disable_siblings;
++ uint8_t disable_cu_siblings = 0;
+ unsigned ApicIdCoreIdSize;
+
+ nb_cfg_54 = 0;
+@@ -1321,14 +1427,23 @@ static void cpu_bus_scan(device_t dev)
+ /* Always use the devicetree node with lapic_id 0 for BSP. */
+ remap_bsp_lapic(cpu_bus);
+
++ if (get_option(&nvram, "compute_unit_siblings") == CB_SUCCESS)
++ disable_cu_siblings = !!nvram;
++
++ if (disable_cu_siblings)
++ printk(BIOS_DEBUG, "Disabling siblings on each compute unit as requested\n");
++
+ for(i = 0; i < nodes; i++) {
+ device_t cdb_dev;
+ unsigned busn, devn;
+ struct bus *pbus;
+
++ uint8_t fam15h = 0;
+ uint8_t rev_gte_d = 0;
+ uint8_t dual_node = 0;
+ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
+
+ busn = CONFIG_CBB;
+ devn = CONFIG_CDB+i;
+@@ -1368,7 +1483,16 @@ static void cpu_bus_scan(device_t dev)
+
+ f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
+
+- if (cpuid_eax(0x80000001) >= 0x8)
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++
++ if (is_fam15h()) {
++ /* Family 15h or later */
++ fam15h = 1;
++ nb_cfg_54 = 1;
++ }
++
++ if ((model >= 0x8) || fam15h)
+ /* Revision D or later */
+ rev_gte_d = 1;
+
+@@ -1378,13 +1502,20 @@ static void cpu_bus_scan(device_t dev)
+ dual_node = 1;
+
+ cores_found = 0; // one core
+- cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3));
++ if (fam15h)
++ cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 5));
++ else
++ cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3));
+ int enable_node = cdb_dev && cdb_dev->enabled;
+ if (enable_node) {
+- j = pci_read_config32(cdb_dev, 0xe8);
+- cores_found = (j >> 12) & 3; // dev is func 3
+- if (siblings > 3)
+- cores_found |= (j >> 13) & 4;
++ if (fam15h) {
++ cores_found = pci_read_config32(cdb_dev, 0x84) & 0xff;
++ } else {
++ j = pci_read_config32(cdb_dev, 0xe8);
++ cores_found = (j >> 12) & 3; // dev is func 3
++ if (siblings > 3)
++ cores_found |= (j >> 13) & 4;
++ }
+ printk(BIOS_DEBUG, " %s siblings=%d\n", dev_path(cdb_dev), cores_found);
+ }
+
+@@ -1404,15 +1535,24 @@ static void cpu_bus_scan(device_t dev)
+
+ if (dual_node) {
+ apic_id = 0;
+- if (nb_cfg_54) {
+- apic_id |= ((i >> 1) & 0x3) << 4; /* Node ID */
++ if (fam15h) {
++ apic_id |= ((i >> 1) & 0x3) << 5; /* Node ID */
+ apic_id |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */
+ } else {
+- apic_id |= i & 0x3; /* Node ID */
+- apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */
++ if (nb_cfg_54) {
++ apic_id |= ((i >> 1) & 0x3) << 4; /* Node ID */
++ apic_id |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */
++ } else {
++ apic_id |= i & 0x3; /* Node ID */
++ apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */
++ }
+ }
+ } else {
+- apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
++ if (fam15h) {
++ apic_id = (i * (siblings + 1)) + j;
++ } else {
++ apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
++ }
+ }
+
+ #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET>0)
+@@ -1422,6 +1562,9 @@ static void cpu_bus_scan(device_t dev)
+ }
+ }
+ #endif
++ if (disable_cu_siblings && (j & 0x1))
++ continue;
++
+ device_t cpu = add_cpu_device(cpu_bus, apic_id, enable_node);
+ if (cpu)
+ amd_cpu_topology(cpu, i, j);
+@@ -1478,6 +1621,6 @@ static void root_complex_enable_dev(struct device *dev)
+ }
+
+ struct chip_operations northbridge_amd_amdfam10_root_complex_ops = {
+- CHIP_NAME("AMD FAM10 Root Complex")
++ CHIP_NAME("AMD Family 10h/15h Root Complex")
+ .enable_dev = root_complex_enable_dev,
+ };
+diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+index 9c2612c..4962c2a 100644
+--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
++++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+@@ -35,8 +35,120 @@ static void print_tf(const char *func, const char *strval)
+ #endif
+ }
+
+-static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq)
++static inline void fam15h_switch_dct(uint32_t dev, uint8_t dct)
+ {
++ uint32_t dword;
++
++ dword = Get_NB32(dev, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ Set_NB32(dev, 0x10c, dword);
++}
++
++static inline void fam15h_switch_nb_pstate_config_reg(uint32_t dev, uint8_t nb_pstate)
++{
++ uint32_t dword;
++
++ dword = Get_NB32(dev, 0x10c);
++ dword &= ~(0x3 << 4);
++ dword |= (nb_pstate & 0x3) << 4;
++ Set_NB32(dev, 0x10c, dword);
++}
++
++static inline uint32_t Get_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ return Get_NB32(dev, reg);
++ } else {
++ return Get_NB32(dev, (0x100 * dct) + reg);
++ }
++}
++
++static inline void Set_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg, uint32_t val)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ Set_NB32(dev, reg, val);
++ } else {
++ Set_NB32(dev, (0x100 * dct) + reg, val);
++ }
++}
++
++static inline uint32_t Get_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate);
++ return Get_NB32(dev, reg);
++ } else {
++ return Get_NB32(dev, (0x100 * dct) + reg);
++ }
++}
++
++static inline void Set_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t val)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate);
++ Set_NB32(dev, reg, val);
++ } else {
++ Set_NB32(dev, (0x100 * dct) + reg, val);
++ }
++}
++
++static inline uint32_t Get_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ return Get_NB32_index_wait(dev, index_reg, index);
++ } else {
++ return Get_NB32_index_wait(dev, (0x100 * dct) + index_reg, index);
++ }
++}
++
++static inline void Set_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index, uint32_t data)
++{
++ if (is_fam15h()) {
++ /* Obtain address of function 0x1 */
++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
++ fam15h_switch_dct(dev_map, dct);
++ Set_NB32_index_wait(dev, index_reg, index, data);
++ } else {
++ Set_NB32_index_wait(dev, (0x100 * dct) + index_reg, index, data);
++ }
++}
++
++static uint16_t voltage_index_to_mv(uint8_t index)
++{
++ if (index & 0x8)
++ return 1150;
++ if (index & 0x4)
++ return 1250;
++ else if (index & 0x2)
++ return 1350;
++ else
++ return 1500;
++}
++
++static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t highest_rank_count, uint8_t registered, uint8_t voltage, uint16_t freq)
++{
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
+ /* Return limited maximum RAM frequency */
+ if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
+ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
+@@ -59,34 +171,178 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq
+ }
+ }
+ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+- if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
+- /* K10 BKDG Rev. 3.62 Table 34 */
+- if (count > 2) {
+- /* Limit to DDR3-800 */
+- if (freq > 400) {
+- freq = 400;
+- print_tf(__func__, ": More than 2 registered DIMMs on channel; limiting to DDR3-800\n");
++ if (voltage == 0) {
++ printk(BIOS_DEBUG, "%s: WARNING: Mainboard DDR3 voltage unknown, assuming 1.5V!\n", __func__);
++ voltage = 0x1;
++ }
++
++ if (is_fam15h()) {
++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
++ /* Fam15h BKDG Rev. 3.14 Table 27 */
++ if (voltage & 0x4) {
++ /* 1.25V */
++ if (count > 1) {
++ if (highest_rank_count > 1) {
++ /* Limit to DDR3-1066 */
++ if (freq > 533) {
++ freq = 533;
++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else if (voltage & 0x2) {
++ /* 1.35V */
++ if (count > 1) {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else if (voltage & 0x1) {
++ /* 1.50V */
++ if (count > 1) {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1866 */
++ if (freq > 933) {
++ freq = 933;
++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ }
++ } else {
++ /* Fam15h BKDG Rev. 3.14 Table 26 */
++ if (voltage & 0x4) {
++ /* 1.25V */
++ if (count > 1) {
++ if (highest_rank_count > 1) {
++ /* Limit to DDR3-1066 */
++ if (freq > 533) {
++ freq = 533;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else if (voltage & 0x2) {
++ /* 1.35V */
++ if (MaxDimmsInstallable > 1) {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else if (voltage & 0x1) {
++ if (MaxDimmsInstallable == 1) {
++ if (count > 1) {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1866 */
++ if (freq > 933) {
++ freq = 933;
++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else {
++ if (count > 1) {
++ if (highest_rank_count > 1) {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ } else {
++ /* Limit to DDR3-1600 */
++ if (freq > 800) {
++ freq = 800;
++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
++ }
++ }
++ }
+ }
+- } else if (count == 2) {
+- /* Limit to DDR3-1066 */
+- if (freq > 533) {
+- freq = 533;
+- print_tf(__func__, ": 2 registered DIMMs on channel; limiting to DDR3-1066\n");
++ }
++ } else {
++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
++ /* K10 BKDG Rev. 3.62 Table 34 */
++ if (count > 2) {
++ /* Limit to DDR3-800 */
++ if (freq > 400) {
++ freq = 400;
++ printk(BIOS_DEBUG, "%s: More than 2 registered DIMMs on %dmV channel; limiting to DDR3-800\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else if (count == 2) {
++ /* Limit to DDR3-1066 */
++ if (freq > 533) {
++ freq = 533;
++ printk(BIOS_DEBUG, "%s: 2 registered DIMMs on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
++ }
++ } else {
++ /* Limit to DDR3-1333 */
++ if (freq > 666) {
++ freq = 666;
++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
++ }
+ }
+ } else {
++ /* K10 BKDG Rev. 3.62 Table 33 */
+ /* Limit to DDR3-1333 */
+ if (freq > 666) {
+ freq = 666;
+- print_tf(__func__, ": 1 registered DIMM on channel; limiting to DDR3-1333\n");
++ printk(BIOS_DEBUG, "%s: unbuffered DIMMs on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+ }
+ }
+- } else {
+- /* K10 BKDG Rev. 3.62 Table 33 */
+- /* Limit to DDR3-1333 */
+- if (freq > 666) {
+- freq = 666;
+- print_tf(__func__, ": unbuffered DIMMs on channel; limiting to DDR3-1333\n");
+- }
+ }
+ }
+
+@@ -216,11 +472,13 @@ void mctGet_DIMMAddr(struct DCTStatStruc *pDCTstat, u32 node)
+
+ }
+
++#if IS_ENABLED(CONFIG_SET_FIDVID)
+ static u8 mctGetProcessorPackageType(void) {
+ /* FIXME: I guess this belongs wherever mctGetLogicalCPUID ends up ? */
+- u32 BrandId = cpuid_ebx(0x80000001);
+- return (u8)((BrandId >> 28) & 0x0F);
++ u32 BrandId = cpuid_ebx(0x80000001);
++ return (u8)((BrandId >> 28) & 0x0F);
+ }
++#endif
+
+ static void raminit_amdmct(struct sys_info *sysinfo)
+ {
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index 97f9db8..8f9177f 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -43,6 +43,7 @@
+ #define CPU_HTNB_FUNC_04 4
+ #define CPU_ADDR_FUNC_01 1
+ #define CPU_NB_FUNC_03 3
++#define CPU_NB_FUNC_05 5
+
+ /* Function 0 registers */
+ #define REG_ROUTE0_0X40 0x40
+@@ -70,6 +71,7 @@
+ #define REG_NB_CPUID_3XFC 0xFC
+ #define REG_NB_LINK_XCS_TOKEN0_3X148 0x148
+ #define REG_NB_DOWNCORE_3X190 0x190
++#define REG_NB_CAPABILITY_5X84 0x84
+
+ /* Function 4 registers */
+
+@@ -555,9 +557,10 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
+ 15, 12, &temp);
+
+ /* bits[15,13,12] specify the cores */
+- /* Support Downcoring */
+ temp = ((temp & 8) >> 1) + (temp & 3);
+ cores = temp + 1;
++
++ /* Support Downcoring */
+ AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
+ makePCIBusFromNode(node),
+ makePCIDeviceFromNode(node),
+@@ -576,6 +579,56 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
+
+ /***************************************************************************//**
+ *
++ * static u8
++ * fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
++ *
++ * Description:
++ * Return the number of cores (1 based count) on node.
++ *
++ * Parameters:
++ * @param[in] node = the node that will be examined
++ * @param[in] *nb = this northbridge
++ * @return = the number of cores
++ *
++ *
++ */
++static u8 fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
++{
++ u32 temp, leveling, cores;
++ u8 i;
++
++ ASSERT((node < nb->maxNodes));
++ /* Read CmpCap [7:0] */
++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_05,
++ REG_NB_CAPABILITY_5X84),
++ 7, 0, &temp);
++
++ /* bits[7:0] specify the cores */
++ temp = temp & 0xff;
++ cores = temp + 1;
++
++ /* Support Downcoring */
++ AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_03,
++ REG_NB_DOWNCORE_3X190),
++ 31, 0, &leveling);
++ for (i=0; i<cores; i++)
++ {
++ if (leveling & ((u32) 1 << i))
++ {
++ temp--;
++ }
++ }
++ return (u8)(temp+1);
++}
++
++/***************************************************************************//**
++ *
+ * static void
+ * setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb)
+ *
+@@ -854,6 +907,69 @@ static BOOL fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
+
+ /***************************************************************************//**
+ *
++ * static BOOL
++ * fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
++ *
++ * Description:
++ * Get node capability and update the minimum supported system capability.
++ * Return whether the current configuration exceeds the capability.
++ *
++ * Parameters:
++ * @param[in] node = the node
++ * @param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered
++ * @param[in] *nb = this northbridge
++ * @return true: system is capable of current config.
++ * false: system is not capable of current config.
++ *
++ * ---------------------------------------------------------------------------------------
++ */
++static BOOL fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
++{
++#ifndef HT_BUILD_NC_ONLY
++ u32 temp;
++ u8 maxNodes;
++
++ ASSERT(node < nb->maxNodes);
++
++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_03,
++ REG_NB_CAPABILITY_3XE8),
++ 18, 16, &temp);
++
++ if (temp != 0)
++ {
++ maxNodes = (1 << (~temp & 0x3)); /* That is, 1, 2, 4, or 8 */
++ }
++ else
++ {
++ /* Check if CPU package is dual node */
++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_03,
++ REG_NB_CAPABILITY_3XE8),
++ 29, 29, &temp);
++ if (temp)
++ maxNodes = 4;
++ else
++ maxNodes = 8;
++ }
++
++ if (pDat->sysMpCap > maxNodes)
++ {
++ pDat->sysMpCap = maxNodes;
++ }
++ /* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */
++ return (pDat->sysMpCap > pDat->NodesDiscovered);
++#else
++ return 1;
++#endif
++}
++
++/***************************************************************************//**
++ *
+ * static void
+ * fam0fStopLink(u8 currentNode, u8 currentLink, cNorthBridge *nb)
+ *
+@@ -2068,6 +2184,49 @@ void newNorthBridge(u8 node, cNorthBridge *nb)
+ u32 match;
+ u32 extFam, baseFam, model;
+
++ cNorthBridge fam15 =
++ {
++#ifdef HT_BUILD_NC_ONLY
++ 8,
++ 1,
++ 12,
++#else
++ 8,
++ 8,
++ 64,
++#endif /* HT_BUILD_NC_ONLY*/
++ writeRoutingTable,
++ writeNodeID,
++ readDefLnk,
++ enableRoutingTables,
++ verifyLinkIsCoherent,
++ readTrueLinkFailStatus,
++ readToken,
++ writeToken,
++ fam15GetNumCoresOnNode,
++ setTotalNodesAndCores,
++ limitNodes,
++ writeFullRoutingTable,
++ isCompatible,
++ fam15IsCapable,
++ (void (*)(u8, u8, cNorthBridge*))commonVoid,
++ (BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse,
++ readSbLink,
++ verifyLinkIsNonCoherent,
++ ht3SetCFGAddrMap,
++ convertBitsToWidth,
++ convertWidthToBits,
++ fam10NorthBridgeFreqMask,
++ gatherLinkData,
++ setLinkData,
++ ht3WriteTrafficDistribution,
++ fam10BufferOptimizations,
++ 0x00000001,
++ 0x00000200,
++ 18,
++ 0x00000f06
++ };
++
+ cNorthBridge fam10 =
+ {
+ #ifdef HT_BUILD_NC_ONLY
+@@ -2175,8 +2334,14 @@ void newNorthBridge(u8 node, cNorthBridge *nb)
+ 7, 4, &model);
+ match = (u32)((baseFam << 8) | extFam);
+
+- /* Test each in turn looking for a match. Init the struct if found */
+- if (match == fam10.compatibleKey)
++ /* Test each in turn looking for a match.
++ * Initialize the struct if found.
++ */
++ if (match == fam15.compatibleKey)
++ {
++ Amdmemcpy((void *)nb, (const void *)&fam15, (u32) sizeof(cNorthBridge));
++ }
++ else if (match == fam10.compatibleKey)
+ {
+ Amdmemcpy((void *)nb, (const void *)&fam10, (u32) sizeof(cNorthBridge));
+ }
+diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c
+index bafda10..0c6b474 100644
+--- a/src/northbridge/amd/amdht/ht_wrapper.c
++++ b/src/northbridge/amd/amdht/ht_wrapper.c
+@@ -172,16 +172,22 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ printk(BIOS_DEBUG, "amd_ht_fixup()\n");
+ if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX)) {
+ uint8_t rev_gte_d = 0;
++ uint8_t fam15h = 0;
+ uint8_t dual_node = 0;
+ uint32_t f3xe8;
+ uint32_t family;
+ uint32_t model;
+
+ family = model = cpuid_eax(0x80000001);
+- model = ((model & 0xf0000) >> 16) | ((model & 0xf0) >> 4);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+- if (model >= 0x8)
+- /* Revision D or later */
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ if ((model >= 0x8) || fam15h)
++ /* Family 10h Revision D or later */
+ rev_gte_d = 1;
+
+ if (rev_gte_d) {
+@@ -193,7 +199,8 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+
+ if (dual_node) {
+ /* Each G34 processor contains a defective HT link.
+- * See the BKDG Rev 3.62 section 2.7.1.5 for details.
++ * See the Family 10h BKDG Rev 3.62 section 2.7.1.5 for details
++ * For Family 15h see the BKDG Rev. 3.14 section 2.12.1.5 for details.
+ */
+ uint8_t node;
+ uint8_t node_count = get_nodes();
+@@ -203,46 +210,46 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
+ printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link\n", node, internal_node_number);
+ if (internal_node_number == 0) {
+- uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xd8) & 0x1;
++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x98:0xd8) & 0x1;
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+- dword = pci_read_config32(NODE_PCI(node, 0), 0xc4);
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4);
+ dword &= ~0x77000000;
+- pci_write_config32(NODE_PCI(node, 0), 0xc4, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4, dword);
+ /* Set Ganged to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 0), 0x178);
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178);
+ dword |= 0x00000001;
+- pci_write_config32(NODE_PCI(node, 0), 0x178, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178, dword);
+ } else {
+ /* Set ConnDly to 1 */
+ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
+ dword |= 0x00000100;
+ pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
+ /* Set TransOff and EndOfChain to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 4), 0xc4);
++ dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4);
+ dword |= 0x000000c0;
+- pci_write_config32(NODE_PCI(node, 4), 0xc4, dword);
++ pci_write_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4, dword);
+ }
+ } else if (internal_node_number == 1) {
+- uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xb8) & 0x1;
++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xf8:0xb8) & 0x1;
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+- dword = pci_read_config32(NODE_PCI(node, 0), 0xa4);
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4);
+ dword &= ~0x77000000;
+- pci_write_config32(NODE_PCI(node, 0), 0xa4, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4, dword);
+ /* Set Ganged to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 0), 0x174);
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174);
+ dword |= 0x00000001;
+- pci_write_config32(NODE_PCI(node, 0), 0x174, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174, dword);
+ } else {
+ /* Set ConnDly to 1 */
+ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
+ dword |= 0x00000100;
+ pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
+ /* Set TransOff and EndOfChain to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 4), 0xa4);
++ dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4);
+ dword |= 0x000000c0;
+- pci_write_config32(NODE_PCI(node, 4), 0xa4, dword);
++ pci_write_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4, dword);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h
+index 117fea5..20a77d3 100644
+--- a/src/northbridge/amd/amdmct/amddefs.h
++++ b/src/northbridge/amd/amdmct/amddefs.h
+@@ -20,33 +20,35 @@
+ /* FIXME: this file should be moved to include/cpu/amd/amddefs.h */
+
+ /* Public Revisions - USE THESE VERSIONS TO MAKE COMPARE WITH CPULOGICALID RETURN VALUE*/
+-#define AMD_SAFEMODE 0x80000000 /* Unknown future revision - SAFE MODE */
+-#define AMD_NPT_F0 0x00000001 /* F0 stepping */
+-#define AMD_NPT_F1 0x00000002 /* F1 stepping */
+-#define AMD_NPT_F2C 0x00000004
+-#define AMD_NPT_F2D 0x00000008
+-#define AMD_NPT_F2E 0x00000010 /* F2 stepping E */
+-#define AMD_NPT_F2G 0x00000020 /* F2 stepping G */
+-#define AMD_NPT_F2J 0x00000040
+-#define AMD_NPT_F2K 0x00000080
+-#define AMD_NPT_F3L 0x00000100 /* F3 Stepping */
+-#define AMD_NPT_G0A 0x00000200 /* G0 stepping */
+-#define AMD_NPT_G1B 0x00000400 /* G1 stepping */
+-#define AMD_DR_A0A 0x00010000 /* Barcelona A0 */
+-#define AMD_DR_A1B 0x00020000 /* Barcelona A1 */
+-#define AMD_DR_A2 0x00040000 /* Barcelona A2 */
+-#define AMD_DR_B0 0x00080000 /* Barcelona B0 */
+-#define AMD_DR_B1 0x00100000 /* Barcelona B1 */
+-#define AMD_DR_B2 0x00200000 /* Barcelona B2 */
+-#define AMD_DR_BA 0x00400000 /* Barcelona BA */
+-#define AMD_DR_B3 0x00800000 /* Barcelona B3 */
+-#define AMD_RB_C2 0x01000000 /* Shanghai C2 */
+-#define AMD_DA_C2 0x02000000 /* XXXX C2 */
+-#define AMD_HY_D0 0x04000000 /* Istanbul D0 */
+-#define AMD_RB_C3 0x08000000 /* ??? C3 */
+-#define AMD_DA_C3 0x10000000 /* XXXX C3 */
+-#define AMD_HY_D1 0x20000000 /* Istanbul D1 */
+-#define AMD_PH_E0 0x40000000 /* Phenom II X4 X6 */
++#define AMD_SAFEMODE 0x8000000000000000 /* Unknown future revision - SAFE MODE */
++#define AMD_NPT_F0 0x0000000000000001 /* F0 stepping */
++#define AMD_NPT_F1 0x0000000000000002 /* F1 stepping */
++#define AMD_NPT_F2C 0x0000000000000004
++#define AMD_NPT_F2D 0x0000000000000008
++#define AMD_NPT_F2E 0x0000000000000010 /* F2 stepping E */
++#define AMD_NPT_F2G 0x0000000000000020 /* F2 stepping G */
++#define AMD_NPT_F2J 0x0000000000000040
++#define AMD_NPT_F2K 0x0000000000000080
++#define AMD_NPT_F3L 0x0000000000000100 /* F3 Stepping */
++#define AMD_NPT_G0A 0x0000000000000200 /* G0 stepping */
++#define AMD_NPT_G1B 0x0000000000000400 /* G1 stepping */
++#define AMD_DR_A0A 0x0000000000010000 /* Barcelona A0 */
++#define AMD_DR_A1B 0x0000000000020000 /* Barcelona A1 */
++#define AMD_DR_A2 0x0000000000040000 /* Barcelona A2 */
++#define AMD_DR_B0 0x0000000000080000 /* Barcelona B0 */
++#define AMD_DR_B1 0x0000000000100000 /* Barcelona B1 */
++#define AMD_DR_B2 0x0000000000200000 /* Barcelona B2 */
++#define AMD_DR_BA 0x0000000000400000 /* Barcelona BA */
++#define AMD_DR_B3 0x0000000000800000 /* Barcelona B3 */
++#define AMD_RB_C2 0x0000000001000000 /* Shanghai C2 */
++#define AMD_DA_C2 0x0000000002000000 /* XXXX C2 */
++#define AMD_HY_D0 0x0000000004000000 /* Istanbul D0 */
++#define AMD_RB_C3 0x0000000008000000 /* ??? C3 */
++#define AMD_DA_C3 0x0000000010000000 /* XXXX C3 */
++#define AMD_HY_D1 0x0000000020000000 /* Istanbul D1 */
++#define AMD_PH_E0 0x0000000040000000 /* Phenom II X4 X6 */
++#define AMD_OR_B2 0x0000000080000000 /* Interlagos */
++#define AMD_OR_C0 0x0000000100000000 /* Abu Dhabi */
+
+ /*
+ * Groups - Create as many as you wish, from the above public values
+@@ -76,6 +78,7 @@
+ #define AMD_DRBH_Cx (AMD_DR_Cx | AMD_HY_D0 )
+ #define AMD_DRBA23_RBC2 (AMD_DR_BA | AMD_DR_B2 | AMD_DR_B3 | AMD_RB_C2 )
+ #define AMD_DR_DAC2_OR_C3 (AMD_DA_C2 | AMD_DA_C3 | AMD_RB_C3)
++#define AMD_FAM15_ALL (AMD_OR_B2 | AMD_OR_C0)
+
+ /*
+ * Public Platforms - USE THESE VERSIONS TO MAKE COMPARE WITH CPUPLATFORMTYPE RETURN VALUE
+@@ -122,23 +125,34 @@
+ */
+ #define CPUID_EXT_PM 0x80000007
+ #define CPUID_MODEL 1
+-#define MCG_CAP 0x00000179
++#define MCG_CAP 0x00000179
+ #define MCG_CTL_P 8
+-#define MC0_CTL 0x00000400
+-#define MC0_STA MC0_CTL + 1
+-#define FS_Base 0xC0000100
++#define MC0_CTL 0x00000400
++#define MC0_STA (MC0_CTL + 1)
++#define MC4_MISC0 0x00000413
++#define MC4_MISC1 0xC0000408
++#define MC4_MISC2 0xC0000409
++#define FS_Base 0xC0000100
+ #define SYSCFG 0xC0010010
+ #define HWCR 0xC0010015
+ #define NB_CFG 0xC001001F
+ #define FidVidStatus 0xC0010042
++#define MC1_CTL_MASK 0xC0010045
+ #define MC4_CTL_MASK 0xC0010048
+ #define OSVW_ID_Length 0xC0010140
+ #define OSVW_Status 0xC0010141
+ #define CPUIDFEATURES 0xC0011004
+ #define LS_CFG 0xC0011020
++#define IC_CFG 0xC0011021
+ #define DC_CFG 0xC0011022
+ #define BU_CFG 0xC0011023
+-#define BU_CFG2 0xC001102A
++#define FP_CFG 0xC0011028
++#define DE_CFG 0xC0011029
++#define BU_CFG2 0xC001102A
++#define BU_CFG3 0xC001102B
++#define EX_CFG 0xC001102C
++#define LS_CFG2 0xC001102D
++#define IBS_OP_DATA3 0xC0011037
+
+ /*
+ * Processor package types
+diff --git a/src/northbridge/amd/amdmct/mct/mct_d.c b/src/northbridge/amd/amdmct/mct/mct_d.c
+index 88910e2..be0af65 100644
+--- a/src/northbridge/amd/amdmct/mct/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct/mct_d.c
+@@ -2189,6 +2189,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
+ for (k = 0; k < SPD_PARTN_LENGTH; k++)
+ pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
++ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
+ pDCTstat->DimmRevisionNumber[i] = 0;
+ for (k = 0; k < 2; k++)
+ pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
+@@ -2206,8 +2207,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ if (byte & JED_REGADCMSK) {
+ RegDIMMPresent |= 1 << i;
+ pDCTstat->DimmRegistered[i] = 1;
+- }
+- else {
++ } else {
+ pDCTstat->DimmRegistered[i] = 0;
+ }
+ /* Check ECC capable */
+diff --git a/src/northbridge/amd/amdmct/mct/mct_d.h b/src/northbridge/amd/amdmct/mct/mct_d.h
+index 132bdc9..6b6194d 100644
+--- a/src/northbridge/amd/amdmct/mct/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct/mct_d.h
+@@ -434,7 +434,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ /* CH A byte lane 0 - 7 maximum filtered window passing DQS delay value*/
+ /* CH B byte lane 0 - 7 minimum filtered window passing DQS delay value*/
+ /* CH B byte lane 0 - 7 maximum filtered window passing DQS delay value*/
+- u32 LogicalCPUID; /* The logical CPUID of the node*/
++ uint64_t LogicalCPUID; /* The logical CPUID of the node*/
+ u16 HostBiosSrvc1; /* Word sized general purpose field for use by host BIOS. Scratch space.*/
+ u32 HostBiosSrvc2; /* Dword sized general purpose field for use by host BIOS. Scratch space.*/
+ u16 DimmQRPresent; /* QuadRank DIMM present?*/
+@@ -529,7 +529,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED];
+
+ uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED];
+- char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH];
++ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
+ uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
+ uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
+ } __attribute__((packed));
+@@ -598,17 +598,18 @@ struct DCTStatStruc { /* A per Node structure*/
+ 266=266MHz (DDR533)
+ 333=333MHz (DDR667)
+ 400=400MHz (DDR800)*/
+-#define NV_ECC_CAP 4 /* Bus ECC capable (1-bits)
++#define NV_MIN_MEMCLK 4 /* Minimum platform demonstrated Memclock (10-bits) */
++#define NV_ECC_CAP 5 /* Bus ECC capable (1-bits)
+ 0=Platform not capable
+ 1=Platform is capable*/
+-#define NV_4RANKType 5 /* Quad Rank DIMM slot type (2-bits)
++#define NV_4RANKType 6 /* Quad Rank DIMM slot type (2-bits)
+ 0=Normal
+ 1=R4 (4-Rank Registered DIMMs in AMD server configuration)
+ 2=S4 (Unbuffered SO-DIMMs)*/
+-#define NV_BYPMAX 6 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
++#define NV_BYPMAX 7 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
+ 4=4 times bypass (normal for non-UMA systems)
+ 7=7 times bypass (normal for UMA systems)*/
+-#define NV_RDWRQBYP 7 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
++#define NV_RDWRQBYP 8 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
+ 2=8 times (normal for non-UMA systems)
+ 3=16 times (normal for UMA systems)*/
+
+@@ -671,8 +672,9 @@ struct DCTStatStruc { /* A per Node structure*/
+ #define NV_ECCRedir 54 /* Dram ECC Redirection enable*/
+ #define NV_DramBKScrub 55 /* Dram ECC Background Scrubber CTL*/
+ #define NV_L2BKScrub 56 /* L2 ECC Background Scrubber CTL*/
+-#define NV_DCBKScrub 57 /* DCache ECC Background Scrubber CTL*/
+-#define NV_CS_SpareCTL 58 /* Chip Select Spare Control bit 0:
++#define NV_L3BKScrub 57 /* L3 ECC Background Scrubber CTL*/
++#define NV_DCBKScrub 58 /* DCache ECC Background Scrubber CTL*/
++#define NV_CS_SpareCTL 59 /* Chip Select Spare Control bit 0:
+ 0=disable Spare
+ 1=enable Spare */
+ /* Chip Select Spare Control bit 1-4:
+@@ -712,7 +714,7 @@ u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass);
+ u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
+ void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+-u32 mctGetLogicalCPUID(u32 Node);
++uint64_t mctGetLogicalCPUID(u32 Node);
+ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u8 Pass);
+ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+diff --git a/src/northbridge/amd/amdmct/mct/mctpro_d.c b/src/northbridge/amd/amdmct/mct/mctpro_d.c
+index c332357..fe56201 100644
+--- a/src/northbridge/amd/amdmct/mct/mctpro_d.c
++++ b/src/northbridge/amd/amdmct/mct/mctpro_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -23,7 +24,7 @@ void EarlySampleSupport_D(void)
+
+ u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val)
+ {
+- u32 tmp;
++ uint64_t tmp;
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+ val &= 0x0FFFFFFF;
+@@ -42,7 +43,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val)
+ * ( F2x[1, 0]8C[1:0] > 00b). Silicon Status: Fixed in Rev B
+ * FIXME: check if this is still required.
+ */
+- u32 tmp;
++ uint64_t tmp;
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+ if(!(val & (3<<12) ))
+@@ -54,7 +55,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val)
+
+ void mct_ForceAutoPrecharge_D(struct DCTStatStruc *pDCTstat, u32 dct)
+ {
+- u32 tmp;
++ uint64_t tmp;
+ u32 reg;
+ u32 reg_off;
+ u32 dev;
+@@ -96,7 +97,7 @@ void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat,
+ * FIXME: check this.
+ */
+
+- u32 tmp;
++ uint64_t tmp;
+ u32 dev;
+ u32 reg;
+ u32 val;
+@@ -143,10 +144,9 @@ void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat,
+ u32 index;
+ u32 reg;
+ u32 val;
+- u32 tmp;
++ uint64_t tmp;
+ u32 Channel;
+
+-
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+
+@@ -206,7 +206,7 @@ u32 Modify_D3CMP(struct DCTStatStruc *pDCTstat, u32 dct, u32 value)
+ u32 index_reg;
+ u32 index;
+ u32 val;
+- u32 tmp;
++ uint64_t tmp;
+
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+@@ -237,7 +237,7 @@ void SyncSetting(struct DCTStatStruc *pDCTstat)
+ * Silicon Status: Fix TBD
+ */
+
+- u32 tmp;
++ uint64_t tmp;
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+ pDCTstat->CH_ODC_CTL[1] = pDCTstat->CH_ODC_CTL[0];
+@@ -278,7 +278,7 @@ u32 CheckNBCOFAutoPrechg(struct DCTStatStruc *pDCTstat, u32 dct)
+
+ void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct)
+ {
+- u32 tmp;
++ uint64_t tmp;
+ u32 Speed;
+ u32 ch, ch_start, ch_end;
+ u32 index_reg;
+@@ -286,7 +286,6 @@ void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct)
+ u32 dev;
+ u32 val;
+
+-
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+ Speed = pDCTstat->Speed;
+@@ -331,7 +330,7 @@ static u8 mct_checkFenceHoleAdjust_D(struct MCTStatStruc *pMCTstat,
+ u8 ChipSel, u8 *result)
+ {
+ u8 ByteLane;
+- u32 tmp;
++ uint64_t tmp;
+
+ tmp = pDCTstat->LogicalCPUID;
+ if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 303c6c7..c73cb26 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -75,6 +75,8 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static u16 Get_Fk_D(u8 k);
+ static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i);
++static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat);
+ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void mct_DramInit(struct MCTStatStruc *pMCTstat,
+@@ -105,11 +107,11 @@ static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
+ static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+- u32 dev, u32 index_reg);
++ u32 dev, uint8_t dct, u32 index_reg);
+ static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, u8 dct,
+ u32 dev, u32 index_reg);
+ static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
+- u32 dev, u32 index_reg, u32 index);
++ u32 dev, uint8_t dct, u32 index_reg, u32 index);
+ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct,
+@@ -128,6 +130,8 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
++static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static u32 mct_NodePresent_D(void);
+@@ -138,7 +142,9 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
+ static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat);
++ struct DCTStatStruc *pDCTstat, u8 dct);
++static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct);
+ void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+@@ -158,6 +164,10 @@ static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct);
+ static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
++static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat);
++void SetTargetFreq(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat);
+
+ static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel);
+@@ -165,7 +175,8 @@ static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dimm);
+ static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2);
+ static void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat);
+-static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
++static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstatA, uint8_t Pass);
+ static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void SyncSetting(struct DCTStatStruc *pDCTstat);
+@@ -173,6 +184,12 @@ static u8 crcCheck(u8 smbaddr);
+ static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
+ static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
+
++static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
++
++static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay,
++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
++
+ /*See mctAutoInitMCT header for index relationships to CL and T*/
+ static const u16 Table_F_k[] = {00,200,266,333,400,533 };
+ static const u8 Tab_BankAddr[] = {0x3F,0x01,0x09,0x3F,0x3F,0x11,0x0A,0x19,0x12,0x1A,0x21,0x22,0x23};
+@@ -223,6 +240,936 @@ static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF};
+ static const u8 Table_Comp_Fall_Slew_20x[] = {7, 5, 3, 2, 0xFF};
+ static const u8 Table_Comp_Fall_Slew_15x[] = {7, 7, 5, 3, 0xFF};
+
++static uint8_t dct_ddr_voltage_index(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t dimm;
++ uint8_t ddr_voltage_index = 0;
++
++ /* Find current DDR supply voltage for this DCT */
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ if (pDCTstat->DIMMValidDCT[dct] & (1 << dimm))
++ ddr_voltage_index |= pDCTstat->DimmConfiguredVoltage[dimm];
++ }
++ if (ddr_voltage_index > 0x7) {
++ printk(BIOS_DEBUG, "%s: Insufficient DDR supply voltage indicated! Configuring processor for 1.25V operation, but this attempt may fail...\n", __func__);
++ ddr_voltage_index = 0x4;
++ }
++ if (ddr_voltage_index == 0x0) {
++ printk(BIOS_DEBUG, "%s: No DDR supply voltage indicated! Configuring processor for 1.5V operation, but this attempt may fail...\n", __func__);
++ ddr_voltage_index = 0x1;
++ }
++
++ return ddr_voltage_index;
++}
++
++static uint16_t fam15h_mhz_to_memclk_config(uint16_t freq)
++{
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++ uint16_t iter;
++
++ /* Compute the index value for the given frequency */
++ for (iter = 0; iter <= 0x16; iter++) {
++ if (fam15h_freq_tab[iter] == freq)
++ break;
++ }
++ if (fam15h_freq_tab[iter] == freq)
++ freq = iter;
++ if (freq == 0)
++ freq = 0x4;
++
++ return freq;
++}
++
++static uint16_t fam10h_mhz_to_memclk_config(uint16_t freq)
++{
++ uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
++ uint16_t iter;
++
++ /* Compute the index value for the given frequency */
++ for (iter = 0; iter <= 0x6; iter++) {
++ if (fam10h_freq_tab[iter] == freq)
++ break;
++ }
++ if (fam10h_freq_tab[iter] == freq)
++ freq = iter;
++ if (freq == 0)
++ freq = 0x3;
++
++ return freq;
++}
++
++static uint16_t mhz_to_memclk_config(uint16_t freq)
++{
++ if (is_fam15h())
++ return fam15h_mhz_to_memclk_config(freq);
++ else
++ return fam10h_mhz_to_memclk_config(freq) + 1;
++}
++
++static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
++{
++ uint8_t lrdimm = 0;
++ uint8_t package_type;
++ uint8_t ddr_voltage_index;
++ uint32_t calibration_code = 0;
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ if (!lrdimm) {
++ /* Not an LRDIMM */
++ if ((package_type == PT_M2) || (package_type == PT_GR)) {
++ /* Socket AM3 or G34 */
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 43 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xdb6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xfff;
++ }
++ }
++ else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 42 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xdb6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xbd6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xdb6;
++ }
++ }
++ else if (ddr_voltage_index & 0x1) {
++ /* 1.5V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 41 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xb6d;
++ }
++ }
++ }
++ else if (package_type == PT_C3) {
++ /* Socket C32 */
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 46 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xdb6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xfff;
++ }
++ }
++ else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 45 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xdb6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xdb6;
++ }
++ }
++ else if (ddr_voltage_index & 0x1) {
++ /* 1.5V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 44 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xfff;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xb6d;
++ }
++ }
++ }
++ } else {
++ /* LRDIMM */
++
++ /* TODO
++ * Implement LRDIMM support
++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Tables 47 - 49
++ */
++ }
++
++ return calibration_code;
++}
++
++static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
++{
++ uint8_t ddr_voltage_index;
++ uint32_t calibration_code = 0;
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 52 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xb64;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xb64;
++ }
++ }
++ else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 51 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ }
++ }
++ else if (ddr_voltage_index & 0x1) {
++ /* 1.5V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 50 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x492;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x492;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x6db;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x6db;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xb6d;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xb6d;
++ }
++ }
++
++ return calibration_code;
++}
++
++static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
++{
++ uint8_t ddr_voltage_index;
++ uint32_t calibration_code = 0;
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 55 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xff6;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xff6;
++ }
++ }
++ else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 54 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xdad;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xdad;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xdad;
++ }
++ }
++ else if (ddr_voltage_index & 0x1) {
++ /* 1.5V */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 53 */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (drive_strength == 0x0)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x1)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x2)
++ calibration_code = 0x924;
++ else if (drive_strength == 0x3)
++ calibration_code = 0x924;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xb6d;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (drive_strength == 0x0)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x1)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x2)
++ calibration_code = 0xff6;
++ else if (drive_strength == 0x3)
++ calibration_code = 0xff6;
++ }
++ }
++
++ return calibration_code;
++}
++
++static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ uint8_t package_type;
++ uint32_t calibration_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00112222;
++ }
++ else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x10112222;
++ }
++ else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x20112222;
++ }
++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ calibration_code = 0x30112222;
++ }
++ else if (MemClkFreq == 0x16) {
++ /* DDR3-1866 */
++ calibration_code = 0x30332222;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00112222;
++ }
++ else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x10112222;
++ }
++ else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x20112222;
++ }
++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ calibration_code = 0x30112222;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x10222222;
++ }
++ else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x20222222;
++ }
++ else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x30222222;
++ }
++ else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x30222222;
++ }
++ else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ calibration_code = 0x30222222;
++ else
++ calibration_code = 0x30112222;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return calibration_code;
++}
++
++static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ uint8_t package_type;
++ uint32_t calibration_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x00383837;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00363635;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00353533;
++ else
++ calibration_code = 0x00003533;
++ } else if (MemClkFreq == 0x16) {
++ /* DDR3-1866 */
++ calibration_code = 0x00333330;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x00383837;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00363635;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00353533;
++ else
++ calibration_code = 0x00003533;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00390039;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x00390039;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x003a3a3a;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00003939;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ calibration_code = 0x00003738;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return calibration_code;
++}
++
++static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ uint8_t package_type;
++ uint32_t slow_access = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
++ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
++ /* DDR3-667 - DDR3-1333 */
++ slow_access = 0;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ if (rank_count_dimm0 == 1)
++ slow_access = 0;
++ else
++ slow_access = 1;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
++ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
++ /* DDR3-667 - DDR3-1333 */
++ slow_access = 0;
++ }
++ else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if (rank_count_dimm0 == 1)
++ slow_access = 0;
++ else
++ slow_access = 1;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
++ || (MemClkFreq == 0xa)) {
++ /* DDR3-667 - DDR3-1066 */
++ slow_access = 0;
++ }
++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ slow_access = 1;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return slow_access;
++}
++
++static void set_2t_configuration(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct)
++{
++ uint32_t dev;
++ uint32_t reg;
++ uint32_t dword;
++
++ uint8_t enable_slow_access_mode = 0;
++ dev = pDCTstat->dev_dct;
++
++ if (is_fam15h()) {
++ if (pDCTstat->_2Tmode)
++ enable_slow_access_mode = 1;
++ } else {
++ if (pDCTstat->_2Tmode == 2)
++ enable_slow_access_mode = 1;
++ }
++
++ reg = 0x94; /* DRAM Configuration High */
++ dword = Get_NB32_DCT(dev, dct, reg);
++ if (enable_slow_access_mode)
++ dword |= (0x1 << 20); /* Set 2T CMD mode */
++ else
++ dword &= ~(0x1 << 20); /* Clear 2T CMD mode */
++ Set_NB32_DCT(dev, dct, reg, dword);
++}
++
++static void precise_ndelay_fam15(struct MCTStatStruc *pMCTstat, uint32_t nanoseconds) {
++ msr_t tsc_msr;
++ uint64_t cycle_count = (((uint64_t)pMCTstat->TSCFreq) * nanoseconds) / 1000;
++ uint64_t start_timestamp;
++ uint64_t current_timestamp;
++
++ tsc_msr = rdmsr(0x00000010);
++ start_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo;
++ do {
++ tsc_msr = rdmsr(0x00000010);
++ current_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo;
++ } while ((current_timestamp - start_timestamp) < cycle_count);
++}
++
++static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t clocks) {
++ uint16_t memclk_freq;
++ uint32_t delay_ns;
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++
++ memclk_freq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ delay_ns = (((uint64_t)clocks * 1000) / fam15h_freq_tab[memclk_freq]);
++ precise_ndelay_fam15(pMCTstat, delay_ns);
++}
++
+ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -277,41 +1224,75 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ restartinit:
+ mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/
+ if (s3resume) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_En_Fam15\n");
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
++ }
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n");
+ restore_mct_information_from_nvram();
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ }
+ } else {
+ NodesWmem = 0;
+ node_sys_base = 0;
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+-
++
+ /* Zero out data structures to avoid false detection of DIMMs */
+ memset(pDCTstat, 0, sizeof(struct DCTStatStruc));
+-
++
+ /* Initialize data structures */
+ pDCTstat->Node_ID = Node;
+ pDCTstat->dev_host = PA_HOST(Node);
+ pDCTstat->dev_map = PA_MAP(Node);
+ pDCTstat->dev_dct = PA_DCT(Node);
+ pDCTstat->dev_nbmisc = PA_NBMISC(Node);
++ pDCTstat->dev_link = PA_LINK(Node);
++ pDCTstat->dev_nbctl = PA_NBCTL(Node);
+ pDCTstat->NodeSysBase = node_sys_base;
+-
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
+ mct_init(pMCTstat, pDCTstat);
+ mctNodeIDDebugPort_D();
+ pDCTstat->NodePresent = NodePresent_D(Node);
+- if (pDCTstat->NodePresent) { /* See if Node is there*/
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n");
+- clear_legacy_Mode(pMCTstat, pDCTstat);
++ if (pDCTstat->NodePresent) {
+ pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
+-
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n");
+ mct_InitialMCT_D(pMCTstat, pDCTstat);
+-
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
+-
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_preInitDCT\n");
++ mct_preInitDCT(pMCTstat, pDCTstat);
++ }
++ node_sys_base = pDCTstat->NodeSysBase;
++ node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
++ }
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
++ DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */
++
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n");
+ mct_initDCT(pMCTstat, pDCTstat);
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+@@ -319,17 +1300,12 @@ restartinit:
+ } else if (pDCTstat->ErrCode < SC_StopError) {
+ NodesWmem++;
+ }
+- } /* if Node present */
+- node_sys_base = pDCTstat->NodeSysBase;
+- node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
++ }
+ }
+ if (NodesWmem == 0) {
+ printk(BIOS_DEBUG, "No Nodes?!\n");
+ goto fatalexit;
+ }
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
+- DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+@@ -351,7 +1327,6 @@ restartinit:
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+ mct_OtherTiming(pMCTstat, pDCTstatA);
+
+-
+ if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
+ goto restartinit;
+ }
+@@ -365,6 +1340,14 @@ restartinit:
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ }
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ }
++
+ mct_FinalMCT_D(pMCTstat, pDCTstatA);
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
+ }
+@@ -404,6 +1387,426 @@ static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
+ return ret;
+ }
+
++/* Enable or disable phy-assisted training mode
++ * Phy-assisted training mode applies to the follow DRAM training procedures:
++ * Write Levelization Training (2.10.5.8.1)
++ * DQS Receiver Enable Training (2.10.5.8.2)
++ */
++static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t enable)
++{
++ uint8_t index;
++ uint32_t dword;
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ if (enable) {
++ /* Enable training mode */
++ dword = Get_NB32_DCT(dev, dct, 0x78); /* DRAM Control */
++ dword &= ~(0x1 << 17); /* AddrCmdTriEn = 0 */
++ Set_NB32_DCT(dev, dct, 0x78, dword); /* DRAM Control */
++
++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */
++ dword |= (0x1 << 18); /* DisAutoRefresh = 1 */
++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */
++
++ dword = Get_NB32_DCT(dev, dct, 0x94); /* DRAM Configuration High */
++ dword &= ~(0xf << 24); /* DcqBypassMax = 0 */
++ dword &= ~(0x1 << 22); /* BankSwizzleMode = 0 */
++ dword &= ~(0x1 << 15); /* PowerDownEn = 0 */
++ dword &= ~(0x3 << 10); /* ZqcsInterval = 0 */
++ Set_NB32_DCT(dev, dct, 0x94, dword); /* DRAM Configuration High */
++
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 0 */
++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 0 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
++
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
++ dword &= ~(0x1 << 12); /* EnRxPadStandby = 0 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
++ }
++
++ dword = Get_NB32_DCT(dev, dct, 0xa4); /* DRAM Controller Temperature Throttle */
++ dword &= ~(0x1 << 11); /* BwCapEn = 0 */
++ dword &= ~(0x1 << 8); /* ODTSEn = 0 */
++ Set_NB32_DCT(dev, dct, 0xa4, dword); /* DRAM Controller Temperature Throttle */
++
++ dword = Get_NB32_DCT(dev, dct, 0x110); /* DRAM Controller Select Low */
++ dword &= ~(0x1 << 2); /* DctSelIntLvEn = 0 */
++ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */
++ dword &= ~(0x1f << 24); /* L3Scrub = 0 */
++ dword &= ~(0x1f); /* DramScrub = 0 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
++ dword &= ~(0x1); /* ScrubReDirEn = 0 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
++ dword |= (0x1 << 4); /* L3ScrbRedirDis = 1 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++
++ /* Fam15h BKDG section 2.10.5.5.1 */
++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
++ dword &= ~(0xf << 24); /* TrdrdSdSc = 0xb */
++ dword |= (0xb << 24);
++ dword &= ~(0xf << 16); /* TrdrdSdDc = 0xb */
++ dword |= (0xb << 16);
++ dword &= ~(0xf); /* TrdrdDd = 0xb */
++ dword |= 0xb;
++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
++
++ /* Fam15h BKDG section 2.10.5.5.2 */
++ dword = Get_NB32_DCT(dev, dct, 0x214); /* DRAM Timing 4 */
++ dword &= ~(0xf << 16); /* TwrwrSdSc = 0xb */
++ dword |= (0xb << 16);
++ dword &= ~(0xf << 8); /* TwrwrSdDc = 0xb */
++ dword |= (0xb << 8);
++ dword &= ~(0xf); /* TwrwrDd = 0xb */
++ dword |= 0xb;
++ Set_NB32_DCT(dev, dct, 0x214, dword); /* DRAM Timing 4 */
++
++ /* Fam15h BKDG section 2.10.5.5.3 */
++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
++ dword &= ~(0xf << 8); /* Twrrd = 0xb */
++ dword |= (0xb << 8);
++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
++
++ /* Fam15h BKDG section 2.10.5.5.4 */
++ dword = Get_NB32_DCT(dev, dct, 0x21c); /* DRAM Timing 6 */
++ dword &= ~(0x1f << 8); /* TrwtTO = 0x16 */
++ dword |= (0x16 << 8);
++ dword &= ~(0x1f << 16); /* TrwtWB = TrwtTO + 1 */
++ dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
++ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */
++ } else {
++ /* Disable training mode */
++ uint8_t lane;
++ uint8_t dimm;
++ uint8_t receiver;
++ uint8_t max_lane;
++ uint8_t ecc_enabled;
++ uint8_t x4_present = 0;
++ uint8_t x8_present = 0;
++ uint8_t memclk_index;
++ uint8_t interleave_channels = 0;
++ uint8_t redirect_ecc_scrub = 0;
++ uint16_t trdrdsddc;
++ uint16_t trdrddd;
++ uint16_t cdd_trdrddd;
++ uint16_t twrwrsddc;
++ uint16_t twrwrdd;
++ uint16_t cdd_twrwrdd;
++ uint16_t twrrd;
++ uint16_t trwtto;
++ uint8_t first_dimm;
++ uint16_t delay;
++ uint16_t delay2;
++ uint8_t read_odt_delay;
++ uint8_t write_odt_delay;
++ uint16_t difference;
++ uint16_t current_total_delay_1[MAX_BYTE_LANES];
++ uint16_t current_total_delay_2[MAX_BYTE_LANES];
++
++ /* FIXME
++ * This should be platform configurable
++ */
++ uint8_t dimm_event_l_pin_support = 0;
++
++ ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
++ if (ecc_enabled)
++ max_lane = 9;
++ else
++ max_lane = 8;
++
++ if (pDCTstat->Dimmx4Present & ((dct)?0xaa:0x55))
++ x4_present = 1;
++ if (pDCTstat->Dimmx8Present & ((dct)?0xaa:0x55))
++ x8_present = 1;
++ memclk_index = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
++
++ if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged))
++ interleave_channels = 1;
++
++ if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
++ redirect_ecc_scrub = 1;
++
++ dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf;
++ if (dword > 6)
++ read_odt_delay = dword - 6;
++ else
++ read_odt_delay = 0;
++
++ dword = Get_NB32_DCT(dev, dct, 0x240);
++ delay = (dword >> 4) & 0xf;
++ if (delay > 6)
++ read_odt_delay = delay - 6;
++ else
++ read_odt_delay = 0;
++ delay = (dword >> 12) & 0x7;
++ if (delay > 6)
++ write_odt_delay = delay - 6;
++ else
++ write_odt_delay = 0;
++
++ /* TODO:
++ * Adjust trdrdsddc if four-rank DIMMs are installed per
++ * section 2.10.5.5.1 of the Family 15h BKDG.
++ * cdd_trdrdsddc will also need to be calculated in that process.
++ */
++ trdrdsddc = 3;
++
++ /* Calculate the Critical Delay Difference for TrdrdDd */
++ cdd_trdrddd = 0;
++ first_dimm = 1;
++ for (receiver = 0; receiver < 8; receiver += 2) {
++ dimm = (receiver >> 1);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
++ continue;
++
++ read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
++
++ if (first_dimm) {
++ memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
++ first_dimm = 0;
++ }
++
++ for (lane = 0; lane < max_lane; lane++) {
++ if (current_total_delay_1[lane] > current_total_delay_2[lane])
++ difference = current_total_delay_1[lane] - current_total_delay_2[lane];
++ else
++ difference = current_total_delay_2[lane] - current_total_delay_1[lane];
++
++ if (difference > cdd_trdrddd)
++ cdd_trdrddd = difference;
++ }
++ }
++
++ /* Convert the difference to MEMCLKs */
++ cdd_trdrddd = (((cdd_trdrddd >> 5) & 0x1f) + 1) / 2;
++
++ /* Calculate Trdrddd */
++ delay = (read_odt_delay + 3) * 2;
++ delay2 = cdd_trdrddd + 7;
++ if (delay2 > delay)
++ delay = delay2;
++ trdrddd = (delay + 1) / 2; /* + 1 is equivalent to ceiling function here */
++ if (trdrdsddc > trdrddd)
++ trdrddd = trdrdsddc;
++
++ /* TODO:
++ * Adjust twrwrsddc if four-rank DIMMs are installed per
++ * section 2.10.5.5.1 of the Family 15h BKDG.
++ * cdd_twrwrsddc will also need to be calculated in that process.
++ */
++ twrwrsddc = 4;
++
++ /* Calculate the Critical Delay Difference for TwrwrDd */
++ cdd_twrwrdd = 0;
++ first_dimm = 1;
++ for (receiver = 0; receiver < 8; receiver += 2) {
++ dimm = (receiver >> 1);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
++ continue;
++
++ read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
++
++
++ if (first_dimm) {
++ memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
++ first_dimm = 0;
++ }
++
++ for (lane = 0; lane < max_lane; lane++) {
++ if (current_total_delay_1[lane] > current_total_delay_2[lane])
++ difference = current_total_delay_1[lane] - current_total_delay_2[lane];
++ else
++ difference = current_total_delay_2[lane] - current_total_delay_1[lane];
++
++ if (difference > cdd_twrwrdd)
++ cdd_twrwrdd = difference;
++ }
++ }
++
++ /* Convert the difference to MEMCLKs */
++ cdd_twrwrdd = (((cdd_twrwrdd >> 5) & 0x1f) + 1) / 2;
++
++ /* Calculate Twrwrdd */
++ delay = (write_odt_delay + 3) * 2;
++ delay2 = cdd_twrwrdd + 7;
++ if (delay2 > delay)
++ delay = delay2;
++ twrwrdd = (delay + 1) / 2; /* + 1 is equivalent to ceiling function here */
++ if (twrwrsddc > twrwrdd)
++ twrwrdd = twrwrsddc;
++
++ dword = Get_NB32_DCT(dev, dct, 0x78); /* DRAM Control */
++ dword |= (0x1 << 17); /* AddrCmdTriEn = 1 */
++ Set_NB32_DCT(dev, dct, 0x78, dword); /* DRAM Control */
++
++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */
++ dword &= ~(0x1 << 18); /* DisAutoRefresh = 0 */
++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */
++
++ dword = Get_NB32_DCT(dev, dct, 0x94); /* DRAM Configuration High */
++ dword |= (0xf << 24); /* DcqBypassMax = 0xf */
++ dword |= (0x1 << 22); /* BankSwizzleMode = 1 */
++ dword |= (0x1 << 15); /* PowerDownEn = 1 */
++ dword &= ~(0x3 << 10); /* ZqcsInterval = 0x2 */
++ dword |= (0x2 << 10);
++ Set_NB32_DCT(dev, dct, 0x94, dword); /* DRAM Configuration High */
++
++ if (x4_present && x8_present) {
++ /* Mixed channel of 4x and 8x DIMMs */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
++ dword &= ~(0x3 << 24); /* RxDLLWakeupTime = 0 */
++ dword &= ~(0x7 << 20); /* RxCPUpdPeriod = 0 */
++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 0 */
++ dword &= ~(0x3 << 8); /* TxDLLWakeupTime = 0 */
++ dword &= ~(0x7 << 4); /* TxCPUpdPeriod = 0 */
++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 0 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
++ } else {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
++ dword &= ~(0x3 << 24); /* RxDLLWakeupTime = 3 */
++ dword |= (0x3 << 24);
++ dword &= ~(0x7 << 20); /* RxCPUpdPeriod = 3 */
++ dword |= (0x3 << 20);
++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 7 */
++ dword |= (0x7 << 16);
++ dword &= ~(0x3 << 8); /* TxDLLWakeupTime = 3 */
++ dword |= (0x3 << 8);
++ dword &= ~(0x7 << 4); /* TxCPUpdPeriod = 3 */
++ dword |= (0x3 << 4);
++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 7 */
++ dword |= 0x7;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
++ }
++
++ if ((memclk_index <= 0x12) && (x4_present != x8_present)) {
++ /* MemClkFreq <= 800MHz
++ * Not a mixed channel of x4 and x8 DIMMs
++ */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
++ dword |= (0x1 << 12); /* EnRxPadStandby = 1 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
++ }
++ } else {
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
++ dword &= ~(0x1 << 12); /* EnRxPadStandby = 0 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
++ }
++ }
++
++ /* TODO
++ * Calculate Twrrd per section 2.10.5.5.3 of the Family 15h BKDG
++ */
++ twrrd = 0xb;
++
++ /* TODO
++ * Calculate TrwtTO per section 2.10.5.5.4 of the Family 15h BKDG
++ */
++ trwtto = 0x16;
++
++ dword = Get_NB32_DCT(dev, dct, 0xa4); /* DRAM Controller Temperature Throttle */
++ dword &= ~(0x1 << 11); /* BwCapEn = 0 */
++ dword &= ~(0x1 << 8); /* ODTSEn = dimm_event_l_pin_support */
++ dword |= (dimm_event_l_pin_support & 0x1) << 8;
++ Set_NB32_DCT(dev, dct, 0xa4, dword); /* DRAM Controller Temperature Throttle */
++
++ dword = Get_NB32_DCT(dev, dct, 0x110); /* DRAM Controller Select Low */
++ dword &= ~(0x1 << 2); /* DctSelIntLvEn = interleave_channels */
++ dword |= (interleave_channels & 0x1) << 2;
++ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */
++ dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
++ dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
++ dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
++ dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
++ dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
++ dword |= redirect_ecc_scrub & 0x1;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
++ dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++
++ /* FIXME
++ * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
++ * Investigate and fix...
++ */
++#if 0
++ /* Fam15h BKDG section 2.10.5.5.1 */
++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
++ dword &= ~(0xf << 24); /* TrdrdSdSc = 0x1 */
++ dword |= (0x1 << 24);
++ dword &= ~(0xf << 16); /* TrdrdSdDc = trdrdsddc */
++ dword |= ((trdrdsddc & 0xf) << 16);
++ dword &= ~(0xf); /* TrdrdDd = trdrddd */
++ dword |= (trdrddd & 0xf);
++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
++#endif
++
++ /* Fam15h BKDG section 2.10.5.5.2 */
++ dword = Get_NB32_DCT(dev, dct, 0x214); /* DRAM Timing 4 */
++ dword &= ~(0xf << 16); /* TwrwrSdSc = 0x1 */
++ dword |= (0x1 << 16);
++ dword &= ~(0xf << 8); /* TwrwrSdDc = twrwrsddc */
++ dword |= ((twrwrsddc & 0xf) << 8);
++ dword &= ~(0xf); /* TwrwrDd = twrwrdd */
++ dword |= (twrwrdd & 0xf);
++ Set_NB32_DCT(dev, dct, 0x214, dword); /* DRAM Timing 4 */
++
++ /* Fam15h BKDG section 2.10.5.5.3 */
++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
++ dword &= ~(0xf << 8); /* Twrrd = twrrd */
++ dword |= ((twrrd & 0xf) << 8);
++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
++
++ /* Fam15h BKDG section 2.10.5.5.4 */
++ dword = Get_NB32_DCT(dev, dct, 0x21c); /* DRAM Timing 6 */
++ dword &= ~(0x1f << 8); /* TrwtTO = trwtto */
++ dword |= ((trwtto & 0x1f) << 8);
++ dword &= ~(0x1f << 16); /* TrwtWB = TrwtTO + 1 */
++ dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
++ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */
++
++ /* Enable prefetchers */
++ dword = Get_NB32_DCT(dev, dct, 0x110); /* Memory Controller Configuration High */
++ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
++ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
++ Set_NB32_DCT(dev, dct, 0x110, dword); /* Memory Controller Configuration High */
++ }
++}
++
++static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstatA)
++{
++ uint8_t node;
++ uint8_t dct;
++
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + node;
++
++ if (pDCTstat->NodePresent)
++ for (dct = 0; dct < 2; dct++)
++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 0);
++ }
++}
++
+ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -420,6 +1823,20 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
+ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
+
++ if (is_fam15h()) {
++ uint8_t Node;
++ struct DCTStatStruc *pDCTstat;
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ pDCTstat = pDCTstatA + Node;
++ if (pDCTstat->NodePresent) {
++ if (pDCTstat->DIMMValidDCT[0])
++ InitPhyCompensation(pMCTstat, pDCTstat, 0);
++ if (pDCTstat->DIMMValidDCT[1])
++ InitPhyCompensation(pMCTstat, pDCTstat, 1);
++ }
++ }
++ }
++
+ if (nv_DQSTrainCTL) {
+ mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
+ /* TODO: should be in mctHookBeforeAnyTraining */
+@@ -427,16 +1844,35 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ _WRMSR(0x26D, 0x04040404, 0x04040404);
+ _WRMSR(0x26E, 0x04040404, 0x04040404);
+ _WRMSR(0x26F, 0x04040404, 0x04040404);
+- mct_WriteLevelization_HW(pMCTstat, pDCTstatA);
++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass);
+
+- TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
++ if (is_fam15h()) {
++ /* Receiver Enable Training Pass 1 */
++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
++ }
+
+- mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, SecondPass);
+
+- /* Second Pass never used for Barcelona! */
+- /* TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); */
++ if (is_fam15h()) {
++ /* Receiver Enable Training Pass 2 */
++ // TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass);
+
+- mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
++ /* TODO:
++ * Determine why running TrainReceiverEn_D in SecondPass
++ * mode yields less stable training values than when run
++ * in FirstPass mode as in the HACK below.
++ */
++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
++ } else {
++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
++ }
++
++ mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
++
++ if (is_fam15h())
++ exit_training_mode_fam15(pMCTstat, pDCTstatA);
++ else
++ mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
+
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+ mctHookAfterAnyTraining();
+@@ -472,7 +1908,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ for (Channel = 0;Channel < 2; Channel++) {
+ /* there are four receiver pairs,
+ loosely associated with chipselects.*/
+- index_reg = 0x98 + Channel * 0x100;
++ index_reg = 0x98;
+ for (Receiver = 0; Receiver < 8; Receiver += 2) {
+ /* Set Receiver Enable Values */
+ mct_SetRcvrEnDly_D(pDCTstat,
+@@ -488,7 +1924,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ txdqs = pDCTstat->CH_D_B_TxDqs[Channel][Receiver >> 1][ByteLane];
+ index = Table_DQSRcvEn_Offset[ByteLane >> 1];
+ index += (Receiver >> 1) * 3 + 0x10 + 0x20; /* Addl_Index */
+- val = Get_NB32_index_wait(dev, 0x98 + 0x100*Channel, index);
++ val = Get_NB32_index_wait_DCT(dev, Channel, 0x98, index);
+ if (ByteLane & 1) { /* odd byte lane */
+ val &= ~(0xFF << 16);
+ val |= txdqs << 16;
+@@ -496,7 +1932,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ val &= ~0xFF;
+ val |= txdqs;
+ }
+- Set_NB32_index_wait(dev, 0x98 + 0x100*Channel, index, val);
++ Set_NB32_index_wait_DCT(dev, Channel, 0x98, index, val);
+ }
+ }
+ }
+@@ -506,7 +1942,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+
+ for (Channel = 0; Channel < 2; Channel++) {
+ u8 *p;
+- index_reg = 0x98 + Channel * 0x100;
++ index_reg = 0x98;
+
+ /* NOTE:
+ * when 400, 533, 667, it will support dimm0/1/2/3,
+@@ -521,7 +1957,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ if (DIMM == 0) {
+ index = 0; /* CHA Write Data Timing Low */
+ } else {
+- if (pDCTstat->Speed >= 4) {
++ if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+ index = 0x100 * DIMM;
+ } else {
+ break;
+@@ -530,23 +1966,23 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ for (Dir = 0; Dir < 2; Dir++) {/* RD/WR */
+ p = pDCTstat->CH_D_DIR_B_DQS[Channel][DIMM][Dir];
+ val = stream_to_int(p); /* CHA Read Data Timing High */
+- Set_NB32_index_wait(dev, index_reg, index+1, val);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+1, val);
+ val = stream_to_int(p+4); /* CHA Write Data Timing High */
+- Set_NB32_index_wait(dev, index_reg, index+2, val);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+2, val);
+ val = *(p+8); /* CHA Write ECC Timing */
+- Set_NB32_index_wait(dev, index_reg, index+3, val);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+3, val);
+ index += 4;
+ }
+ }
+ }
+
+ for (Channel = 0; Channel<2; Channel++) {
+- reg = 0x78 + Channel * 0x100;
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, Channel, reg);
+ val &= ~(0x3ff<<22);
+ val |= ((u32) pDCTstat->CH_MaxRdLat[Channel] << 22);
+ val &= ~(1<<DqsRcvEnTrain);
+- Set_NB32(dev, reg, val); /* program MaxRdLatency to correspond with current delay*/
++ Set_NB32_DCT(dev, Channel, reg, val); /* program MaxRdLatency to correspond with current delay*/
+ }
+ }
+ }
+@@ -808,49 +2244,70 @@ finish:
+ return ret;
+ }
+
+-static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
++static void DCTPreInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ /*
+- * Initialize DRAM on single Athlon 64/Opteron Node.
++ * Run DCT pre-initialization tasks
+ */
+- u8 stopDCTflag;
+- u32 val;
++ uint32_t dword;
+
++ /* Reset DCT registers */
+ ClearDCT_D(pMCTstat, pDCTstat, dct);
+- stopDCTflag = 1; /*preload flag with 'disable' */
+- /* enable DDR3 support */
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100);
+- val |= 1 << Ddr3Mode;
+- Set_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100, val);
++ pDCTstat->stopDCT = 1; /*preload flag with 'disable' */
++
++ if (!is_fam15h()) {
++ /* Enable DDR3 support */
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
++ dword |= 1 << Ddr3Mode;
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
++ }
++
++ /* Read the SPD information into the data structures */
+ if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) {
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_DIMMPresence Done\n");
+- if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
+- if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n");
+- if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
+- if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n");
+- stopDCTflag = 0;
+- if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n");
+- StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */
+- }
++ }
++}
++
++static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
++{
++ /*
++ * Initialize DRAM on single Athlon 64/Opteron Node.
++ */
++ uint32_t dword;
++
++ if (!is_fam15h()) {
++ /* (Re)-enable DDR3 support */
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
++ dword |= 1 << Ddr3Mode;
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
++ }
++
++ if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
++ if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n");
++ if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
++ if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n");
++ pDCTstat->stopDCT = 0;
++ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n");
++ StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */
+ }
+ }
+ }
+ }
+ }
+
+- if (stopDCTflag) {
+- u32 reg_off = dct * 0x100;
+- val = 1<<DisDramInterface;
+- Set_NB32(pDCTstat->dev_dct, reg_off+0x94, val);
+- /*To maximize power savings when DisDramInterface=1b,
+- all of the MemClkDis bits should also be set.*/
+- val = 0xFF000000;
+- Set_NB32(pDCTstat->dev_dct, reg_off+0x88, val);
++ if (pDCTstat->stopDCT) {
++ dword = 1 << DisDramInterface;
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
++
++ /* To maximize power savings when DisDramInterface=1b,
++ * all of the MemClkDis bits should also be set.
++ */
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x88, 0xff000000);
+ } else {
+ mct_EnDllShutdownSR(pMCTstat, pDCTstat, dct);
+ }
+@@ -872,20 +2329,24 @@ static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat = pDCTstatA + Node;
+ mct_SyncDCTsReady(pDCTstat);
+ }
+- /* v6.1.3 */
+- /* re-enable phy compensation engine when dram init is completed on all nodes. */
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
+- if (pDCTstat->NodePresent) {
+- if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) {
+- /* re-enable phy compensation engine when dram init on both DCTs is completed. */
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8);
+- val &= ~(1 << DisAutoComp);
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val);
++
++ if (!is_fam15h()) {
++ /* v6.1.3 */
++ /* re-enable phy compensation engine when dram init is completed on all nodes. */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++ if (pDCTstat->NodePresent) {
++ if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) {
++ /* re-enable phy compensation engine when dram init on both DCTs is completed. */
++ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8);
++ val &= ~(1 << DisAutoComp);
++ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val);
++ }
+ }
+ }
+ }
++
+ /* wait 750us before any memory access can be made. */
+ mct_Wait(15000);
+ }
+@@ -907,10 +2368,9 @@ static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
+ */
+ u32 val;
+ u32 dev;
+- u32 reg_off = dct * 0x100;
+
+ dev = pDCTstat->dev_dct;
+- val = Get_NB32(dev, 0x94 + reg_off);
++ val = Get_NB32_DCT(dev, dct, 0x94);
+ if (val & (1<<MemClkFreqVal)) {
+ mctHookBeforeDramInit(); /* generalized Hook */
+ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)))
+@@ -925,23 +2385,23 @@ static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
+ {
+ u32 reg_end;
+ u32 dev = pDCTstat->dev_dct;
+- u32 reg = 0x40 + 0x100 * dct;
++ u32 reg = 0x40;
+ u32 val = 0;
+
+ if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+- reg_end = 0x78 + 0x100 * dct;
++ reg_end = 0x78;
+ } else {
+- reg_end = 0xA4 + 0x100 * dct;
++ reg_end = 0xA4;
+ }
+
+ while(reg < reg_end) {
+ if ((reg & 0xFF) == 0x90) {
+ if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+- val = Get_NB32(dev, reg); /* get DRAMConfigLow */
++ val = Get_NB32_DCT(dev, dct, reg); /* get DRAMConfigLow */
+ val |= 0x08000000; /* preserve value of DisDllShutdownSR for only Rev.D */
+ }
+ }
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ val = 0;
+ reg += 4;
+ }
+@@ -960,6 +2420,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ u16 Trp, Trrd, Trcd, Tras, Trc;
+ u8 Trfc[4];
+ u16 Tfaw;
++ u16 Tcwl; /* Fam15h only */
+ u32 DramTimingLo, DramTimingHi;
+ u8 tCK16x;
+ u16 Twtr;
+@@ -968,10 +2429,11 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ u8 byte;
+ u32 dword;
+ u32 dev;
+- u32 reg_off;
+ u32 val;
+ u16 smbaddr;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* Gather all DIMM mini-max values for cycle timing data */
+ Trp = 0;
+ Trrd = 0;
+@@ -1184,88 +2646,164 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+
+ mctAdjustAutoCycTmg_D();
+
++ if (is_fam15h()) {
++ /* Compute Tcwl (Fam15h BKDG v3.14 Table 203) */
++ if (pDCTstat->Speed <= 0x6)
++ Tcwl = 0x5;
++ else if (pDCTstat->Speed == 0xa)
++ Tcwl = 0x6;
++ else if (pDCTstat->Speed == 0xe)
++ Tcwl = 0x7;
++ else if (pDCTstat->Speed == 0x12)
++ Tcwl = 0x8;
++ else if (pDCTstat->Speed == 0x16)
++ Tcwl = 0x9;
++ else
++ Tcwl = 0x5; /* Power-on default */
++ }
++
+ /* Program DRAM Timing values */
+- DramTimingLo = 0; /* Dram Timing Low init */
+- val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
+- DramTimingLo |= val;
++ if (is_fam15h()) {
++ dev = pDCTstat->dev_dct;
+
+- val = pDCTstat->Trcd - Bias_TrcdT;
+- DramTimingLo |= val<<4;
++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */
++ val = 2; /* Tref = 7.8us */
++ dword &= ~(0x3 << 16);
++ dword |= (val & 0x3) << 16;
++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */
++
++ dword = Get_NB32_DCT(dev, dct, 0x200); /* DRAM Timing 0 */
++ dword &= ~(0x3f1f1f1f);
++ dword |= ((pDCTstat->Tras + 0xf) & 0x3f) << 24; /* Tras */
++ dword |= ((pDCTstat->Trp + 0x5) & 0x1f) << 16; /* Trp */
++ dword |= ((pDCTstat->Trcd + 0x5) & 0x1f) << 8; /* Trcd */
++ dword |= (pDCTstat->CASL & 0x1f); /* Tcl */
++ Set_NB32_DCT(dev, dct, 0x200, dword); /* DRAM Timing 0 */
++
++ dword = Get_NB32_DCT(dev, dct, 0x204); /* DRAM Timing 1 */
++ dword &= ~(0x0f3f0f3f);
++ dword |= ((pDCTstat->Trtp + 0x4) & 0xf) << 24; /* Trtp */
++ if (pDCTstat->Tfaw != 0)
++ dword |= ((((pDCTstat->Tfaw - 0x1) * 2) + 0x10) & 0x3f) << 16; /* FourActWindow */
++ dword |= ((pDCTstat->Trrd + 0x4) & 0xf) << 8; /* Trrd */
++ dword |= ((pDCTstat->Trc + 0xb) & 0x3f); /* Trc */
++ Set_NB32_DCT(dev, dct, 0x204, dword); /* DRAM Timing 1 */
++
++ dword = Get_NB32_DCT(dev, dct, 0x208); /* DRAM Timing 2 */
++ dword &= ~(0x07070707);
++ dword |= (pDCTstat->Trfc[3] & 0x7) << 24; /* Trfc3 */
++ dword |= (pDCTstat->Trfc[2] & 0x7) << 16; /* Trfc2 */
++ dword |= (pDCTstat->Trfc[1] & 0x7) << 8; /* Trfc1 */
++ dword |= (pDCTstat->Trfc[0] & 0x7); /* Trfc0 */
++ Set_NB32_DCT(dev, dct, 0x208, dword); /* DRAM Timing 2 */
++
++ dword = Get_NB32_DCT(dev, dct, 0x20c); /* DRAM Timing 3 */
++ dword &= ~(0x00000f00);
++ dword |= ((pDCTstat->Twtr + 0x4) & 0xf) << 8; /* Twtr */
++ dword &= ~(0x0000001f);
++ dword |= (Tcwl & 0x1f); /* Tcwl */
++ Set_NB32_DCT(dev, dct, 0x20c, dword); /* DRAM Timing 3 */
++
++ dword = Get_NB32_DCT(dev, dct, 0x22c); /* DRAM Timing 10 */
++ dword &= ~(0x0000001f);
++ dword |= ((pDCTstat->Twr + 0x4) & 0x1f); /* Twr */
++ Set_NB32_DCT(dev, dct, 0x22c, dword); /* DRAM Timing 10 */
++
++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
++ /* Enable phy-assisted training mode */
++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1);
++ }
+
+- val = pDCTstat->Trp - Bias_TrpT;
+- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+- DramTimingLo |= val<<7;
++ /* Other setup (not training specific) */
++ dword = Get_NB32_DCT(dev, dct, 0x90); /* DRAM Configuration Low */
++ dword &= ~(0x1 << 23); /* ForceAutoPchg = 0 */
++ dword &= ~(0x1 << 20); /* DynPageCloseEn = 0 */
++ Set_NB32_DCT(dev, dct, 0x90, dword); /* DRAM Configuration Low */
+
+- val = pDCTstat->Trtp - Bias_TrtpT;
+- DramTimingLo |= val<<10;
++ Set_NB32_DCT(dev, dct, 0x228, 0x14141414); /* DRAM Timing 9 */
++ } else {
++ DramTimingLo = 0; /* Dram Timing Low init */
++ val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
++ DramTimingLo |= val;
+
+- val = pDCTstat->Tras - Bias_TrasT;
+- DramTimingLo |= val<<12;
++ val = pDCTstat->Trcd - Bias_TrcdT;
++ DramTimingLo |= val<<4;
+
+- val = pDCTstat->Trc - Bias_TrcT;
+- DramTimingLo |= val<<16;
++ val = pDCTstat->Trp - Bias_TrpT;
++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
++ DramTimingLo |= val<<7;
+
+- val = pDCTstat->Trrd - Bias_TrrdT;
+- DramTimingLo |= val<<22;
++ val = pDCTstat->Trtp - Bias_TrtpT;
++ DramTimingLo |= val<<10;
+
+- DramTimingHi = 0; /* Dram Timing High init */
+- val = pDCTstat->Twtr - Bias_TwtrT;
+- DramTimingHi |= val<<8;
++ val = pDCTstat->Tras - Bias_TrasT;
++ DramTimingLo |= val<<12;
+
+- val = 2;
+- DramTimingHi |= val<<16;
++ val = pDCTstat->Trc - Bias_TrcT;
++ DramTimingLo |= val<<16;
+
+- val = 0;
+- for (i=4;i>0;i--) {
+- val <<= 3;
+- val |= Trfc[i-1];
+- }
+- DramTimingHi |= val << 20;
++ val = pDCTstat->Trrd - Bias_TrrdT;
++ DramTimingLo |= val<<22;
+
+- dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * dct;
+- /* Twr */
+- val = pDCTstat->Twr;
+- if (val == 10)
+- val = 9;
+- else if (val == 12)
+- val = 10;
+- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+- val -= Bias_TwrT;
+- val <<= 4;
+- dword = Get_NB32(dev, 0x84 + reg_off);
+- dword &= ~0x70;
+- dword |= val;
+- Set_NB32(dev, 0x84 + reg_off, dword);
++ DramTimingHi = 0; /* Dram Timing High init */
++ val = pDCTstat->Twtr - Bias_TwtrT;
++ DramTimingHi |= val<<8;
+
+- /* Tfaw */
+- val = pDCTstat->Tfaw;
+- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+- val -= Bias_TfawT;
+- val >>= 1;
+- val <<= 28;
+- dword = Get_NB32(dev, 0x94 + reg_off);
+- dword &= ~0xf0000000;
+- dword |= val;
+- Set_NB32(dev, 0x94 + reg_off, dword);
+-
+- /* dev = pDCTstat->dev_dct; */
+- /* reg_off = 0x100 * dct; */
+-
+- if (pDCTstat->Speed > 4) {
+- val = Get_NB32(dev, 0x88 + reg_off);
+- val &= 0xFF000000;
+- DramTimingLo |= val;
+- }
+- Set_NB32(dev, 0x88 + reg_off, DramTimingLo); /*DCT Timing Low*/
++ val = 2; /* Tref = 7.8us */
++ DramTimingHi |= val<<16;
++
++ val = 0;
++ for (i=4;i>0;i--) {
++ val <<= 3;
++ val |= Trfc[i-1];
++ }
++ DramTimingHi |= val << 20;
+
+- if (pDCTstat->Speed > 4) {
+- DramTimingHi |= 1 << DisAutoRefresh;
++ dev = pDCTstat->dev_dct;
++ /* Twr */
++ val = pDCTstat->Twr;
++ if (val == 10)
++ val = 9;
++ else if (val == 12)
++ val = 10;
++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
++ val -= Bias_TwrT;
++ val <<= 4;
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ dword &= ~0x70;
++ dword |= val;
++ Set_NB32_DCT(dev, dct, 0x84, dword);
++
++ /* Tfaw */
++ val = pDCTstat->Tfaw;
++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
++ val -= Bias_TfawT;
++ val >>= 1;
++ val <<= 28;
++ dword = Get_NB32_DCT(dev, dct, 0x94);
++ dword &= ~0xf0000000;
++ dword |= val;
++ Set_NB32_DCT(dev, dct, 0x94, dword);
++
++ /* dev = pDCTstat->dev_dct; */
++
++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
++ val = Get_NB32_DCT(dev, dct, 0x88);
++ val &= 0xFF000000;
++ DramTimingLo |= val;
++ }
++ Set_NB32_DCT(dev, dct, 0x88, DramTimingLo); /*DCT Timing Low*/
++
++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
++ DramTimingHi |= 1 << DisAutoRefresh;
++ }
++ DramTimingHi |= 0x000018FF;
++ Set_NB32_DCT(dev, dct, 0x8c, DramTimingHi); /*DCT Timing Hi*/
+ }
+- DramTimingHi |= 0x000018FF;
+- Set_NB32(dev, 0x8c + reg_off, DramTimingHi); /*DCT Timing Hi*/
+
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+@@ -1299,6 +2837,8 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+ * timing mode is 'Auto'.
+ */
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* Get primary timing (CAS Latency and Cycle Time) */
+ if (pDCTstat->Speed == 0) {
+ mctGet_MaxLoadFreq(pDCTstat);
+@@ -1308,6 +2848,7 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+
+ /* Go get best T and CL as specified by DIMM mfgs. and OEM */
+ SPDGetTCL_D(pMCTstat, pDCTstat, dct);
++
+ /* skip callback mctForce800to1067_D */
+ pDCTstat->Speed = pDCTstat->DIMMAutoSpeed;
+ pDCTstat->CASL = pDCTstat->DIMMCASL;
+@@ -1340,7 +2881,10 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ u16 word;
+
+ /* Get CPU Si Revision defined limit (NPT) */
+- proposedFreq = 800; /* Rev F0 programmable max memclock is */
++ if (is_fam15h())
++ proposedFreq = 933;
++ else
++ proposedFreq = 800; /* Rev F0 programmable max memclock is */
+
+ /*Get User defined limit if "limit" mode */
+ if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) {
+@@ -1377,6 +2921,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ u16 tCKmin16x;
+ u16 tCKproposed16x;
+ u8 CLactual, CLdesired, CLT_Fail;
++ uint16_t min_frequency_tck16x;
+
+ u8 smbaddr, byte = 0, bytex = 0;
+
+@@ -1386,6 +2931,17 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ tCKmin16x = 0;
+ CLT_Fail = 0;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
++ if (is_fam15h()) {
++ uint16_t minimum_frequency_mhz = mctGet_NVbits(NV_MIN_MEMCLK);
++ if (minimum_frequency_mhz == 0)
++ minimum_frequency_mhz = 333;
++ min_frequency_tck16x = 16000 / minimum_frequency_mhz;
++ } else {
++ min_frequency_tck16x = 40;
++ }
++
+ for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
+ if (pDCTstat->DIMMValid & (1 << i)) {
+ smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+@@ -1415,27 +2971,44 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ tCKmin16x = byte * MTB16x;
+ }
+ }
+- /* calculate tCKproposed16x */
++ /* calculate tCKproposed16x (proposed clock period in ns * 16) */
+ tCKproposed16x = 16000 / pDCTstat->PresetmaxFreq;
+ if (tCKmin16x > tCKproposed16x)
+ tCKproposed16x = tCKmin16x;
+
+- /* mctHookTwo1333DimmOverride(); */
+- /* For UDIMM, if there are two DDR3-1333 on the same channel,
+- downgrade DDR speed to 1066. */
+-
+ /* TODO: get user manual tCK16x(Freq.) and overwrite current tCKproposed16x if manual. */
+- if (tCKproposed16x == 20)
+- pDCTstat->TargetFreq = 7;
+- else if (tCKproposed16x <= 24) {
+- pDCTstat->TargetFreq = 6;
+- tCKproposed16x = 24;
+- } else if (tCKproposed16x <= 30) {
+- pDCTstat->TargetFreq = 5;
+- tCKproposed16x = 30;
++ if (is_fam15h()) {
++ if (tCKproposed16x == 17)
++ pDCTstat->TargetFreq = 0x16;
++ else if (tCKproposed16x <= 20) {
++ pDCTstat->TargetFreq = 0x12;
++ tCKproposed16x = 20;
++ } else if (tCKproposed16x <= 24) {
++ pDCTstat->TargetFreq = 0xe;
++ tCKproposed16x = 24;
++ } else if (tCKproposed16x <= 30) {
++ pDCTstat->TargetFreq = 0xa;
++ tCKproposed16x = 30;
++ } else if (tCKproposed16x <= 40) {
++ pDCTstat->TargetFreq = 0x6;
++ tCKproposed16x = 40;
++ } else {
++ pDCTstat->TargetFreq = 0x4;
++ tCKproposed16x = 48;
++ }
+ } else {
+- pDCTstat->TargetFreq = 4;
+- tCKproposed16x = 40;
++ if (tCKproposed16x == 20)
++ pDCTstat->TargetFreq = 7;
++ else if (tCKproposed16x <= 24) {
++ pDCTstat->TargetFreq = 6;
++ tCKproposed16x = 24;
++ } else if (tCKproposed16x <= 30) {
++ pDCTstat->TargetFreq = 5;
++ tCKproposed16x = 30;
++ } else {
++ pDCTstat->TargetFreq = 4;
++ tCKproposed16x = 40;
++ }
+ }
+ /* Running through this loop twice:
+ - First time find tCL at target frequency
+@@ -1474,27 +3047,42 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ /* get CL and T */
+ if (!CLT_Fail) {
+ bytex = CLactual;
+- if (tCKproposed16x == 20)
+- byte = 7;
+- else if (tCKproposed16x == 24)
+- byte = 6;
+- else if (tCKproposed16x == 30)
+- byte = 5;
+- else
+- byte = 4;
++ if (is_fam15h()) {
++ if (tCKproposed16x == 17)
++ byte = 0x16;
++ else if (tCKproposed16x == 20)
++ byte = 0x12;
++ else if (tCKproposed16x == 24)
++ byte = 0xe;
++ else if (tCKproposed16x == 30)
++ byte = 0xa;
++ else if (tCKproposed16x == 40)
++ byte = 0x6;
++ else
++ byte = 0x4;
++ } else {
++ if (tCKproposed16x == 20)
++ byte = 7;
++ else if (tCKproposed16x == 24)
++ byte = 6;
++ else if (tCKproposed16x == 30)
++ byte = 5;
++ else
++ byte = 4;
++ }
+ } else {
+ /* mctHookManualCLOverride */
+ /* TODO: */
+ }
+
+- if (tCKproposed16x != 40) {
++ if (tCKproposed16x != min_frequency_tck16x) {
+ if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+ pDCTstat->DIMMAutoSpeed = byte;
+ pDCTstat->DIMMCASL = bytex;
+ break;
+ } else {
+ pDCTstat->TargetCASL = bytex;
+- tCKproposed16x = 40;
++ tCKproposed16x = min_frequency_tck16x;
+ }
+ } else {
+ pDCTstat->DIMMAutoSpeed = byte;
+@@ -1515,29 +3103,21 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 dev;
+- u32 reg;
+- u32 val;
++ if (!is_fam15h()) {
++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
+
+- mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
++ if (pDCTstat->GangedMode == 1) {
++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
++ }
+
+- if (pDCTstat->GangedMode == 1) {
+- mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
+- mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
+- }
++ set_2t_configuration(pMCTstat, pDCTstat, dct);
+
+- if ( pDCTstat->_2Tmode == 2) {
+- dev = pDCTstat->dev_dct;
+- reg = 0x94 + 0x100 * dct; /* Dram Configuration Hi */
+- val = Get_NB32(dev, reg);
+- val |= 1 << 20; /* 2T CMD mode */
+- Set_NB32(dev, reg, val);
++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
++ mct_PlatformSpec(pMCTstat, pDCTstat, dct);
++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
++ InitPhyCompensation(pMCTstat, pDCTstat, dct);
+ }
+-
+- mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
+- mct_PlatformSpec(pMCTstat, pDCTstat, dct);
+- if (pDCTstat->DIMMAutoSpeed == 4)
+- InitPhyCompensation(pMCTstat, pDCTstat, dct);
+ mctHookAfterPSCfg();
+
+ return pDCTstat->ErrCode;
+@@ -1549,11 +3129,11 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ u32 DramControl, DramTimingLo, Status;
+ u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2;
+ u32 val;
+- u32 reg_off;
+ u32 dev;
+ u16 word;
+ u32 dword;
+ u8 byte;
++ uint32_t offset;
+
+ DramConfigLo = 0;
+ DramConfigHi = 0;
+@@ -1573,12 +3153,10 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ Status = pDCTstat->Status;
+
+ dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * dct;
+-
+
+ /* Build Dram Control Register Value */
+- DramConfigMisc2 = Get_NB32 (dev, 0xA8 + reg_off); /* Dram Control*/
+- DramControl = Get_NB32 (dev, 0x78 + reg_off); /* Dram Control*/
++ DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8); /* Dram Control*/
++ DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control*/
+
+ /* FIXME: Skip mct_checkForDxSupport */
+ /* REV_CALL mct_DoRdPtrInit if not Dx */
+@@ -1620,8 +3198,12 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ DramConfigLo = mct_DisDllShutdownSR(pMCTstat, pDCTstat, DramConfigLo, dct);
+
+ /* Build Dram Config Hi Register Value */
++ if (is_fam15h())
++ offset = 0x0;
++ else
++ offset = 0x1;
+ dword = pDCTstat->Speed;
+- DramConfigHi |= dword - 1; /* get MemClk encoding */
++ DramConfigHi |= dword - offset; /* get MemClk encoding */
+ DramConfigHi |= 1 << MemClkFreqVal;
+
+ if (Status & (1 << SB_Registered))
+@@ -1654,7 +3236,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ val = 0x0f; /* recommended setting (default) */
+ DramConfigHi |= val << 24;
+
+- if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx))
++ if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx | AMD_FAM15_ALL))
+ DramConfigHi |= 1 << DcqArbBypassEn;
+
+ /* Build MemClkDis Value from Dram Timing Lo and
+@@ -1665,7 +3247,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ NV_AllMemClks <>0 AND SB_DiagClks ==0 */
+
+ /* Dram Timing Low (owns Clock Enable bits) */
+- DramTimingLo = Get_NB32(dev, 0x88 + reg_off);
++ DramTimingLo = Get_NB32_DCT(dev, dct, 0x88);
+ if (mctGet_NVbits(NV_AllMemClks) == 0) {
+ /* Special Jedec SPD diagnostic bit - "enable all clocks" */
+ if (!(pDCTstat->Status & (1<<SB_DiagClks))) {
+@@ -1696,28 +3278,34 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ }
+ dword++ ;
+ }
++ DramTimingLo &= ~(0xff << 24);
+ DramTimingLo |= byte << 24;
+ }
+ }
+
+- printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %x\n", DramControl);
+- printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %x\n", DramTimingLo);
+- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %x\n", DramConfigMisc);
+- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %x\n", DramConfigMisc2);
+- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %x\n", DramConfigLo);
+- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %x\n", DramConfigHi);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %08x\n", DramControl);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %08x\n", DramTimingLo);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %08x\n", DramConfigMisc);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %08x\n", DramConfigMisc2);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %08x\n", DramConfigLo);
++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %08x\n", DramConfigHi);
+
+ /* Write Values to the registers */
+- Set_NB32(dev, 0x78 + reg_off, DramControl);
+- Set_NB32(dev, 0x88 + reg_off, DramTimingLo);
+- Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc);
++ Set_NB32_DCT(dev, dct, 0x78, DramControl);
++ Set_NB32_DCT(dev, dct, 0x88, DramTimingLo);
++ Set_NB32_DCT(dev, dct, 0xa0, DramConfigMisc);
+ DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2);
+- Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2);
+- Set_NB32(dev, 0x90 + reg_off, DramConfigLo);
++ Set_NB32_DCT(dev, dct, 0xa8, DramConfigMisc2);
++ Set_NB32_DCT(dev, dct, 0x90, DramConfigLo);
+ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+- dword = Get_NB32(dev, 0x94 + reg_off);
++
++ if (is_fam15h())
++ InitDDRPhy(pMCTstat, pDCTstat, dct);
++
++ /* Write the DRAM Configuration High register, including memory frequency change */
++ dword = Get_NB32_DCT(dev, dct, 0x94);
+ DramConfigHi |= dword;
+- mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi);
++ mct_SetDramConfigHi_D(pMCTstat, pDCTstat, dct, DramConfigHi);
+ mct_EarlyArbEn_D(pMCTstat, pDCTstat, dct);
+ mctHookAfterAutoCfg();
+
+@@ -1727,6 +3315,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ printk(BIOS_DEBUG, "AutoConfig: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "AutoConfig: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "AutoConfig: Done\n\n");
++
+ AutoConfig_exit:
+ return pDCTstat->ErrCode;
+ }
+@@ -1744,14 +3333,12 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ u32 val;
+ u32 reg;
+ u32 dev;
+- u32 reg_off;
+ u8 byte;
+ u16 word;
+ u32 dword;
+ u16 smbaddr;
+
+ dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * dct;
+
+ BankAddrReg = 0;
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel+=2) {
+@@ -1816,10 +3403,10 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ /*set ChipSelect population indicator odd bits*/
+ pDCTstat->CSPresent |= 1 << (ChipSel + 1);
+
+- reg = 0x60+(ChipSel<<1) + reg_off; /*Dram CS Mask Register */
++ reg = 0x60+(ChipSel<<1); /*Dram CS Mask Register */
+ val = csMask;
+ val &= 0x1FF83FE0; /* Mask out reserved bits.*/
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ } else {
+ if (pDCTstat->DIMMSPDCSE & (1<<ChipSel))
+ pDCTstat->CSTestFail |= (1<<ChipSel);
+@@ -1843,8 +3430,8 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ if (!pDCTstat->CSPresent)
+ pDCTstat->ErrCode = SC_StopError;
+
+- reg = 0x80 + reg_off; /* Bank Addressing Register */
+- Set_NB32(dev, reg, BankAddrReg);
++ reg = 0x80; /* Bank Addressing Register */
++ Set_NB32_DCT(dev, dct, reg, BankAddrReg);
+
+ pDCTstat->CSPresent_DCT[dct] = pDCTstat->CSPresent;
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+@@ -1929,11 +3516,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ u16 word;
+ u32 dev;
+ u32 reg;
+- u32 reg_off;
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * dct;
+
+ _DSpareEn = 0;
+
+@@ -1970,11 +3555,11 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ BiggestBank = 0;
+ for (q = 0; q < MAX_CS_SUPPORTED; q++) { /* from DIMMS to CS */
+ if (pDCTstat->CSPresent & (1 << q)) { /* bank present? */
+- reg = 0x40 + (q << 2) + reg_off; /* Base[q] reg.*/
+- val = Get_NB32(dev, reg);
++ reg = 0x40 + (q << 2); /* Base[q] reg.*/
++ val = Get_NB32_DCT(dev, dct, reg);
+ if (!(val & 3)) { /* (CSEnable|Spare==1)bank is enabled already? */
+- reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/
+- val = Get_NB32(dev, reg);
++ reg = 0x60 + (q << 1); /*Mask[q] reg.*/
++ val = Get_NB32_DCT(dev, dct, reg);
+ val >>= 19;
+ val++;
+ val <<= 19;
+@@ -1990,7 +3575,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ if (BiggestBank !=0) {
+ curcsBase = nxtcsBase; /* curcsBase=nxtcsBase*/
+ /* DRAM CS Base b Address Register offset */
+- reg = 0x40 + (b << 2) + reg_off;
++ reg = 0x40 + (b << 2);
+ if (_DSpareEn) {
+ BiggestBank = 0;
+ val = 1 << Spare; /* Spare Enable*/
+@@ -2009,7 +3594,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+ }
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ if (_DSpareEn)
+ _DSpareEn = 0;
+ else
+@@ -2020,9 +3605,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ /* bank present but disabled?*/
+ if ( pDCTstat->CSTestFail & (1 << p)) {
+ /* DRAM CS Base b Address Register offset */
+- reg = (p << 2) + 0x40 + reg_off;
++ reg = (p << 2) + 0x40;
+ val = 1 << TestFail;
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ }
+ }
+
+@@ -2060,7 +3645,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ u16 i, j, k;
+ u8 smbaddr;
+ u8 SPDCtrl;
+- u16 RegDIMMPresent, MaxDimms;
++ u16 RegDIMMPresent, LRDIMMPresent, MaxDimms;
+ u8 devwidth;
+ u16 DimmSlots;
+ u8 byte = 0, bytex;
+@@ -2073,6 +3658,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT);
+
+ RegDIMMPresent = 0;
++ LRDIMMPresent = 0;
+ pDCTstat->DimmQRPresent = 0;
+
+ for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
+@@ -2111,6 +3697,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
+ for (k = 0; k < SPD_PARTN_LENGTH; k++)
+ pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
++ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
+ pDCTstat->DimmRevisionNumber[i] = 0;
+ for (k = 0; k < 2; k++)
+ pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
+@@ -2134,6 +3721,12 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ pDCTstat->DimmRegistered[i] = 0;
+ }
++ if (byte == JED_LRDIMM) {
++ LRDIMMPresent |= 1 << i;
++ pDCTstat->DimmLoadReduced[i] = 1;
++ } else {
++ pDCTstat->DimmLoadReduced[i] = 0;
++ }
+ /* Check ECC capable */
+ byte = mctRead_SPD(smbaddr, SPD_BusWidth);
+ if (byte & JED_ECC) {
+@@ -2217,6 +3810,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ printk(BIOS_DEBUG, "\t DIMMPresence: DIMMValid=%x\n", pDCTstat->DIMMValid);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DIMMPresent=%x\n", pDCTstat->DIMMPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: RegDIMMPresent=%x\n", RegDIMMPresent);
++ printk(BIOS_DEBUG, "\t DIMMPresence: LRDIMMPresent=%x\n", LRDIMMPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DimmECCPresent=%x\n", pDCTstat->DimmECCPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DimmPARPresent=%x\n", pDCTstat->DimmPARPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx4Present=%x\n", pDCTstat->Dimmx4Present);
+@@ -2243,6 +3837,16 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->Status |= 1<<SB_Registered;
+ }
+ }
++ if (LRDIMMPresent != 0) {
++ if ((LRDIMMPresent ^ pDCTstat->DIMMValid) !=0) {
++ /* module type DIMM mismatch (reg'ed, unbuffered) */
++ pDCTstat->ErrStatus |= 1<<SB_DimmMismatchM;
++ pDCTstat->ErrCode = SC_StopError;
++ } else{
++ /* all DIMMs are registered */
++ pDCTstat->Status |= 1<<SB_LoadReduced;
++ }
++ }
+ if (pDCTstat->DimmECCPresent != 0) {
+ if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) {
+ /* all DIMMs are ECC capable */
+@@ -2280,6 +3884,26 @@ static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i)
+ return p[i];
+ }
+
++static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ u8 err_code;
++
++ /* Preconfigure DCT0 */
++ DCTPreInit_D(pMCTstat, pDCTstat, 0);
++
++ /* Configure DCT1 if unganged and enabled*/
++ if (!pDCTstat->GangedMode) {
++ if (pDCTstat->DIMMValidDCT[1] > 0) {
++ err_code = pDCTstat->ErrCode; /* save DCT0 errors */
++ pDCTstat->ErrCode = 0;
++ DCTPreInit_D(pMCTstat, pDCTstat, 1);
++ if (pDCTstat->ErrCode == 2) /* DCT1 is not Running */
++ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */
++ }
++ }
++}
++
+ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+@@ -2291,7 +3915,7 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+ /* Do nothing goto exitDCTInit; any fatal errors? */
+ } else {
+- /* Configure DCT1 if unganged and enabled*/
++ /* Configure DCT1 if unganged and enabled */
+ if (!pDCTstat->GangedMode) {
+ if (pDCTstat->DIMMValidDCT[1] > 0) {
+ err_code = pDCTstat->ErrCode; /* save DCT0 errors */
+@@ -2301,17 +3925,21 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */
+ } else {
+ val = 1 << DisDramInterface;
+- Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
++
++ /* To maximize power savings when DisDramInterface=1b,
++ * all of the MemClkDis bits should also be set.
++ */
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x88, 0xff000000);
+ }
+ }
+ }
+-/* exitDCTInit: */
+ }
+
+ static void mct_DramInit(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat);
++ mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat, dct);
+ mct_DramInit_Sw_D(pMCTstat, pDCTstat, dct);
+ /* mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); */
+ }
+@@ -2339,7 +3967,8 @@ static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
+ if (byte)
+ pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); /* Set temp. to avoid setting of ganged mode */
+
+- if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) {
++ if ((!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) && (pDCTstat->LogicalCPUID & AMD_FAM10_ALL)) {
++ /* Ganged channel mode not supported on Family 15h or higher */
+ pDCTstat->GangedMode = 1;
+ /* valid 128-bit mode population. */
+ pDCTstat->Status |= 1 << SB_128bitmode;
+@@ -2383,10 +4012,8 @@ void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data)
+
+ u32 Get_NB32_index_wait(u32 dev, u32 index_reg, u32 index)
+ {
+-
+ u32 dword;
+
+-
+ index &= ~(1 << DctAccessWrite);
+ Set_NB32(dev, index_reg, index);
+ do {
+@@ -2401,7 +4028,6 @@ void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data)
+ {
+ u32 dword;
+
+-
+ Set_NB32(dev, index_reg + 0x4, data);
+ index |= (1 << DctAccessWrite);
+ Set_NB32(dev, index_reg, index);
+@@ -2416,16 +4042,17 @@ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
+ {
+ /* mct_checkForCxDxSupport_D */
+ if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) {
++ /* Family 10h Errata 322: Address and Command Fine Delay Values May Be Incorrect */
+ /* 1. Write 00000000h to F2x[1,0]9C_xD08E000 */
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0x0D08E000, 0);
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D08E000, 0);
+ /* 2. If DRAM Configuration Register[MemClkFreq] (F2x[1,0]94[2:0]) is
+ greater than or equal to 011b (DDR-800 and higher),
+ then write 00000080h to F2x[1,0]9C_xD02E001,
+ else write 00000090h to F2x[1,0]9C_xD02E001. */
+- if (pDCTstat->Speed >= 4)
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x80);
++ if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x80);
+ else
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x90);
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x90);
+ }
+ return pDCTstat->ErrCode;
+ }
+@@ -2451,9 +4078,9 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ i_end = dct + 1;
+ }
+ for (i=i_start; i<i_end; i++) {
+- index_reg = 0x98 + (i * 0x100);
+- Set_NB32_index_wait(dev, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
+- Set_NB32_index_wait(dev, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
++ index_reg = 0x98;
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
+ }
+
+ return pDCTstat->ErrCode;
+@@ -2507,14 +4134,14 @@ static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,
+ }
+
+ if (pDCTstat->DIMMValidDCT[0] == 0) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+ val |= 1 << DisDramInterface;
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
+ }
+ if (pDCTstat->DIMMValidDCT[1] == 0) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+ val |= 1 << DisDramInterface;
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
+ }
+
+ printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status);
+@@ -2644,21 +4271,20 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u32 reg;
+- u32 reg_off = 0x100 * dct;
+ u32 val;
+ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+
+- Get_DqsRcvEnGross_Diff(pDCTstat, dev, 0x98 + reg_off);
+- Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98 + reg_off);
++ Get_DqsRcvEnGross_Diff(pDCTstat, dev, dct, 0x98);
++ Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98);
+ Get_Trdrd(pMCTstat, pDCTstat, dct);
+ Get_Twrwr(pMCTstat, pDCTstat, dct);
+ Get_Twrrd(pMCTstat, pDCTstat, dct);
+ Get_TrwtTO(pMCTstat, pDCTstat, dct);
+ Get_TrwtWB(pMCTstat, pDCTstat);
+
+- reg = 0x8C + reg_off; /* Dram Timing Hi */
+- val = Get_NB32(dev, reg);
++ reg = 0x8C; /* Dram Timing Hi */
++ val = Get_NB32_DCT(dev, dct, reg);
+ val &= 0xffff0300;
+ dword = pDCTstat->TrwtTO;
+ val |= dword << 4;
+@@ -2670,10 +4296,10 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
+ val |= dword << 14;
+ dword = pDCTstat->TrwtWB;
+ val |= dword;
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+
+- reg = 0x78 + reg_off;
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, dct, reg);
+ val &= 0xFFFFC0FF;
+ dword = pDCTstat->Twrrd >> 2;
+ val |= dword << 8;
+@@ -2681,7 +4307,7 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
+ val |= dword << 10;
+ dword = pDCTstat->Trdrd >> 2;
+ val |= dword << 12;
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ }
+
+ static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
+@@ -2751,18 +4377,17 @@ static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
+ static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val1, val2;
+
+- val1 = Get_NB32(dev, reg_off + 0x88) & 0xF;
+- val2 = (Get_NB32(dev, reg_off + 0x84) >> 20) & 7;
++ val1 = Get_NB32_DCT(dev, dct, 0x88) & 0xF;
++ val2 = (Get_NB32_DCT(dev, dct, 0x84) >> 20) & 7;
+
+ return val1 - val2;
+ }
+
+ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+- u32 dev, u32 index_reg)
++ u32 dev, uint8_t dct, u32 index_reg)
+ {
+ u8 Smallest, Largest;
+ u32 val;
+@@ -2772,12 +4397,12 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+ DqsRcvEnGrossDelay of any other DIMM is equal to the Critical
+ Gross Delay Difference (CGDD) */
+ /* DqsRcvEn byte 1,0 */
+- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x10);
++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x10);
+ Largest = val & 0xFF;
+ Smallest = (val >> 8) & 0xFF;
+
+ /* DqsRcvEn byte 3,2 */
+- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11);
++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x11);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+@@ -2786,7 +4411,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+ Largest = byte;
+
+ /* DqsRcvEn byte 5,4 */
+- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20);
++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x20);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+@@ -2795,7 +4420,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+ Largest = byte;
+
+ /* DqsRcvEn byte 7,6 */
+- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21);
++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x21);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+@@ -2805,7 +4430,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+
+ if (pDCTstat->DimmECCPresent> 0) {
+ /*DqsRcvEn Ecc */
+- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12);
++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x12);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+@@ -2869,7 +4494,7 @@ static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat,
+ }
+
+ static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
+- u32 dev, u32 index_reg,
++ u32 dev, uint8_t dct, u32 index_reg,
+ u32 index)
+ {
+ u8 Smallest, Largest;
+@@ -2887,7 +4512,7 @@ static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
+
+ for (i=0; i < 8; i+=2) {
+ if ( pDCTstat->DIMMValid & (1 << i)) {
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ val &= 0x00E000E0;
+ byte = (val >> 5) & 0xFF;
+ if (byte < Smallest)
+@@ -2925,7 +4550,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
+ Smallest = 3;
+ Largest = 0;
+ for (i=0; i < 2; i++) {
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ val &= 0x60606060;
+ val >>= 5;
+ for (j=0; j < 4; j++) {
+@@ -2941,7 +4566,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
+
+ if (pDCTstat->DimmECCPresent > 0) {
+ index++;
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ val &= 0x00000060;
+ val >>= 5;
+ byte = val & 0xFF;
+@@ -2961,25 +4586,30 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
+ static void mct_PhyController_Config(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 index_reg = 0x98 + 0x100 * dct;
++ uint8_t index;
++ uint32_t dword;
++ u32 index_reg = 0x98;
+ u32 dev = pDCTstat->dev_dct;
+- u32 val;
+
+- if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3)) {
++ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3 | AMD_FAM15_ALL)) {
+ if (pDCTstat->Dimmx4Present == 0) {
+- /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for power saving */
+- val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0F13); /* Agesa v3 v6 might be wrong here. */
+- val |= 1 << 7; /* BIOS should set this bit when x4 DIMMs are not present */
+- Set_NB32_index_wait(dev, index_reg, 0x0D0F0F13, val);
++ /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for
++ * additional power saving when x4 DIMMs are not present.
++ */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8));
++ dword |= (0x1 << 7); /* RxDqsUDllPowerDown = 1 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8), dword);
++ }
+ }
+ }
+
+- if (pDCTstat->LogicalCPUID & AMD_DR_DAC2_OR_C3) {
++ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_FAM15_ALL)) {
+ if (pDCTstat->DimmECCPresent == 0) {
+ /* Set bit4 PwrDn to register F2x[1, 0]98_x0D0F0830 for power saving */
+- val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0830);
+- val |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */
+- Set_NB32_index_wait(dev, index_reg, 0x0D0F0830, val);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830);
++ dword |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830, dword);
+ }
+ }
+
+@@ -3020,21 +4650,61 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ val &= ~(1 << 12);
+
+ val &= 0x0FFFFFFF;
+- switch (pDCTstat->Speed) {
+- case 4:
+- val |= 0x50000000; /* 5 for DDR800 */
+- break;
+- case 5:
+- val |= 0x60000000; /* 6 for DDR1066 */
+- break;
+- case 6:
+- val |= 0x80000000; /* 8 for DDR800 */
+- break;
+- default:
+- val |= 0x90000000; /* 9 for DDR1600 */
+- break;
++ if (!is_fam15h()) {
++ switch (pDCTstat->Speed) {
++ case 4:
++ val |= 0x50000000; /* 5 for DDR800 */
++ break;
++ case 5:
++ val |= 0x60000000; /* 6 for DDR1066 */
++ break;
++ case 6:
++ val |= 0x80000000; /* 8 for DDR800 */
++ break;
++ default:
++ val |= 0x90000000; /* 9 for DDR1600 */
++ break;
++ }
+ }
+ Set_NB32(pDCTstat->dev_dct, 0x1B0, val);
++
++ if (is_fam15h()) {
++ uint8_t wm1;
++ uint8_t wm2;
++
++ switch (pDCTstat->Speed) {
++ case 0x4:
++ wm1 = 0x3;
++ wm2 = 0x4;
++ break;
++ case 0x6:
++ wm1 = 0x3;
++ wm2 = 0x5;
++ break;
++ case 0xa:
++ wm1 = 0x4;
++ wm2 = 0x6;
++ break;
++ case 0xe:
++ wm1 = 0x5;
++ wm2 = 0x8;
++ break;
++ case 0x12:
++ wm1 = 0x6;
++ wm2 = 0x9;
++ break;
++ default:
++ wm1 = 0x7;
++ wm2 = 0xa;
++ break;
++ }
++
++ val = Get_NB32(pDCTstat->dev_dct, 0x1B4);
++ val &= ~(0x3ff);
++ val |= ((wm2 & 0x1f) << 5);
++ val |= (wm1 & 0x1f);
++ Set_NB32(pDCTstat->dev_dct, 0x1B4, val);
++ }
+ }
+ }
+
+@@ -3051,16 +4721,103 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ /* Force the NB P-state to P0 */
++ uint32_t dword;
++ uint32_t dword2;
++
++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x174);
++ if (!(dword & 0x1)) {
++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
++ pDCTstat->SwNbPstateLoDis = (dword >> 14) & 0x1;
++ pDCTstat->NbPstateDisOnP0 = (dword >> 13) & 0x1;
++ pDCTstat->NbPstateThreshold = (dword >> 9) & 0x7;
++ pDCTstat->NbPstateHi = (dword >> 6) & 0x3;
++ dword &= ~(0x1 << 14); /* SwNbPstateLoDis = 0 */
++ dword &= ~(0x1 << 13); /* NbPstateDisOnP0 = 0 */
++ dword &= ~(0x7 << 9); /* NbPstateThreshold = 0 */
++ dword &= ~(0x3 << 3); /* NbPstateLo = NbPstateMaxVal */
++ dword |= ((dword & 0x3) << 3);
++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
++
++ /* Wait until CurNbPState == NbPstateLo */
++ do {
++ dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174);
++ } while (((dword2 << 19) & 0x7) != (dword & 0x3));
++
++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
++ dword &= ~(0x3 << 6); /* NbPstateHi = 0 */
++ dword |= (0x3 << 14); /* SwNbPstateLoDis = 1 */
++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
++
++ /* Wait until CurNbPState == 0 */
++ do {
++ dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174);
++ } while (((dword2 << 19) & 0x7) != 0);
++ }
++}
++
++void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ /* Restore normal NB P-state functionailty */
++ uint32_t dword;
++
++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x174);
++ if (!(dword & 0x1)) {
++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
++ dword &= ~(0x1 << 14); /* SwNbPstateLoDis*/
++ dword |= ((pDCTstat->SwNbPstateLoDis & 0x1) << 14);
++ dword &= ~(0x1 << 13); /* NbPstateDisOnP0 */
++ dword |= ((pDCTstat->NbPstateDisOnP0 & 0x1) << 13);
++ dword &= ~(0x7 << 9); /* NbPstateThreshold */
++ dword |= ((pDCTstat->NbPstateThreshold & 0x7) << 9);
++ dword &= ~(0x3 << 6); /* NbPstateHi */
++ dword |= ((pDCTstat->NbPstateHi & 0x3) << 3);
++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
++ }
++}
++
+ static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+ {
+- mct_SetClToNB_D(pMCTstat, pDCTstat);
+- mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
++ if (is_fam15h()) {
++ msr_t p0_state_msr;
++ uint8_t cpu_fid;
++ uint8_t cpu_did;
++ uint32_t cpu_divisor;
++ uint8_t boost_states;
++
++ /* Retrieve the number of boost states */
++ boost_states = (Get_NB32(pDCTstat->dev_link, 0x15c) >> 2) & 0x7;
++
++ /* Retrieve and store the TSC frequency (P0 COF) */
++ p0_state_msr = rdmsr(0xc0010064 + boost_states);
++ cpu_fid = p0_state_msr.lo & 0x3f;
++ cpu_did = (p0_state_msr.lo >> 6) & 0x7;
++ cpu_divisor = (0x1 << cpu_did);
++ pMCTstat->TSCFreq = (100 * (cpu_fid + 0x10)) / cpu_divisor;
++
++ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
++ } else {
++ /* K10 BKDG v3.62 section 2.8.9.2 */
++ printk(BIOS_DEBUG, "mct_InitialMCT_D: clear_legacy_Mode\n");
++ clear_legacy_Mode(pMCTstat, pDCTstat);
++
++ /* Northbridge configuration */
++ mct_SetClToNB_D(pMCTstat, pDCTstat);
++ mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
++ }
+ }
+
+ static u32 mct_NodePresent_D(void)
+ {
+ u32 val;
+- val = 0x12001022;
++ if (is_fam15h())
++ val = 0x16001022;
++ else
++ val = 0x12001022;
+ return val;
+ }
+
+@@ -3093,14 +4850,13 @@ static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
+
+ /* Clear Legacy BIOS Mode bit */
+ reg = 0x94;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 0, reg);
+ val &= ~(1<<LegacyBiosMode);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 0, reg, val);
+
+- reg = 0x94 + 0x100;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 1, reg);
+ val &= ~(1<<LegacyBiosMode);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 1, reg, val);
+ }
+
+ static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
+@@ -3167,7 +4923,7 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+ u32 dev = pDCTstat->dev_dct;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u32 index;
+ u16 word;
+
+@@ -3182,9 +4938,9 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat,
+ }
+ word = (~word) & 0xFF;
+ index = 0x0c;
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ val |= word;
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+ }
+
+ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+@@ -3192,7 +4948,7 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+ u32 dev;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u32 index;
+ u16 word;
+
+@@ -3204,14 +4960,14 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ word = pDCTstat->CSPresent;
+
+ index = 0x0c;
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ if ((word & 0x55) == 0)
+ val |= 1 << 12;
+
+ if ((word & 0xAA) == 0)
+ val |= 1 << 13;
+
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+ }
+
+ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+@@ -3219,7 +4975,7 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+ u32 dev;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u8 cs;
+ u32 index;
+ u8 odt;
+@@ -3253,86 +5009,281 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+ }
+
+ index = 0x0C;
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+ val |= ((odt & 0xFF) << 8); /* set bits 11:8 ODTTriState[3:0] */
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
++
++}
++
++/* Family 15h */
++static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct)
++{
++ uint8_t index;
++ uint32_t dword;
++ uint8_t ddr_voltage_index;
++ uint8_t amd_voltage_level_index = 0;
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
++ /* Find current DDR supply voltage for this DCT */
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++
++ /* Fam15h BKDG v3.14 section 2.10.5.3
++ * The remainder of the Phy Initialization algorithm picks up in phyAssistedMemFnceTraining
++ */
++ for (dct = 0; dct < 2; dct++) {
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
++
++ /* Program desired VDDIO level */
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ amd_voltage_level_index = 0x2;
++ } else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ amd_voltage_level_index = 0x1;
++ } else if (ddr_voltage_index & 0x1) {
++ /* 1.50V */
++ amd_voltage_level_index = 0x0;
++ }
++
++ /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
++ }
++
++ /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
++ for (index = 0; index < 0x3; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
++ }
++ for (index = 0; index < 0x2; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
++ }
++ for (index = 0; index < 0x1; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
++ }
++
++ /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009);
++ dword &= ~(0x0000c00c);
++ dword |= (amd_voltage_level_index << 14);
++ dword |= (amd_voltage_level_index << 2);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009, dword);
++ }
+
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u8 i;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u32 dev = pDCTstat->dev_dct;
+- u32 val;
+ u32 valx = 0;
+- u32 dword;
++ uint8_t index;
++ uint32_t dword;
+ const u8 *p;
+
+- val = Get_NB32_index_wait(dev, index_reg, 0x00);
+- dword = 0;
+- for (i=0; i < 6; i++) {
+- switch (i) {
+- case 0:
+- case 4:
+- p = Table_Comp_Rise_Slew_15x;
+- valx = p[(val >> 16) & 3];
+- break;
+- case 1:
+- case 5:
+- p = Table_Comp_Fall_Slew_15x;
+- valx = p[(val >> 16) & 3];
+- break;
+- case 2:
+- p = Table_Comp_Rise_Slew_20x;
+- valx = p[(val >> 8) & 3];
+- break;
+- case 3:
+- p = Table_Comp_Fall_Slew_20x;
+- valx = p[(val >> 8) & 3];
+- break;
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
++ if (is_fam15h()) {
++ /* Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 */
++ uint32_t tx_pre;
++ uint32_t drive_strength;
++
++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
++ dword |= (0x3 << 13);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
++
++ /* Determine TxPreP/TxPreN for data lanes (Stage 1) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 20) & 0x7; /* DqsDrvStren */
++ tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for data lanes (Stage 1) */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8));
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8), dword);
++ }
+
++ /* Determine TxPreP/TxPreN for data lanes (Stage 2) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 16) & 0x7; /* DataDrvStren */
++ tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for data lanes (Stage 2) */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8));
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8), dword);
++ }
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8));
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8), dword);
+ }
+- dword |= valx << (5 * i);
+- }
+
+- /* Override/Exception */
+- if (!pDCTstat->GangedMode) {
+- i = 0; /* use i for the dct setting required */
+- if (pDCTstat->MAdimms[0] < 4)
+- i = 1;
+- if (((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) && (pDCTstat->MAdimms[i] == 4)) {
+- dword &= 0xF18FFF18;
+- index_reg = 0x98; /* force dct = 0 */
++ /* Determine TxPreP/TxPreN for command/address lines (Stage 1) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 4) & 0x7; /* CsOdtDrvStren */
++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for command/address lines (Stage 1) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002, dword);
++
++ /* Determine TxPreP/TxPreN for command/address lines (Stage 2) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 8) & 0x7; /* AddrCmdDrvStren */
++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for command/address lines (Stage 2) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012);
++ dword &= ~(0xfff);
++ dword |= tx_pre;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102, dword);
++
++ /* Determine TxPreP/TxPreN for command/address lines (Stage 3) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 0) & 0x7; /* CkeDrvStren */
++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for command/address lines (Stage 3) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002, dword);
++
++ /* Determine TxPreP/TxPreN for clock lines */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
++ drive_strength = (dword >> 12) & 0x7; /* ClkDrvStren */
++ tx_pre = fam15h_phy_predriver_clk_calibration_code(pDCTstat, dct, drive_strength);
++
++ /* Program TxPreP/TxPreN for clock lines */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102, dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202);
++ dword &= ~(0xfff);
++ dword |= (0x8000 | tx_pre);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202, dword);
++ } else {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00);
++ dword = 0;
++ for (i=0; i < 6; i++) {
++ switch (i) {
++ case 0:
++ case 4:
++ p = Table_Comp_Rise_Slew_15x;
++ valx = p[(dword >> 16) & 3];
++ break;
++ case 1:
++ case 5:
++ p = Table_Comp_Fall_Slew_15x;
++ valx = p[(dword >> 16) & 3];
++ break;
++ case 2:
++ p = Table_Comp_Rise_Slew_20x;
++ valx = p[(dword >> 8) & 3];
++ break;
++ case 3:
++ p = Table_Comp_Fall_Slew_20x;
++ valx = p[(dword >> 8) & 3];
++ break;
++ }
++ dword |= valx << (5 * i);
+ }
++
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0a, dword);
+ }
+
+- Set_NB32_index_wait(dev, index_reg, 0x0a, dword);
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 reg;
+- u32 val;
+- u32 dev = pDCTstat->dev_dct;
+-
+- /* GhEnhancement #18429 modified by askar: For low NB CLK :
+- * Memclk ratio, the DCT may need to arbitrate early to avoid
+- * unnecessary bubbles.
+- * bit 19 of F2x[1,0]78 Dram Control Register, set this bit only when
+- * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive)
+- */
+- reg = 0x78 + 0x100 * dct;
+- val = Get_NB32(dev, reg);
+-
+- if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
+- val |= (1 << EarlyArbEn);
+- else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
+- val |= (1 << EarlyArbEn);
+-
+- Set_NB32(dev, reg, val);
++ if (!is_fam15h()) {
++ u32 reg;
++ u32 val;
++ u32 dev = pDCTstat->dev_dct;
++
++ /* GhEnhancement #18429 modified by askar: For low NB CLK :
++ * Memclk ratio, the DCT may need to arbitrate early to avoid
++ * unnecessary bubbles.
++ * bit 19 of F2x[1,0]78 Dram Control Register, set this bit only when
++ * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive)
++ */
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, dct, reg);
++
++ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
++ val |= (1 << EarlyArbEn);
++ else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
++ val |= (1 << EarlyArbEn);
++
++ Set_NB32_DCT(dev, dct, reg, val);
++ }
+ }
+
+ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+@@ -3355,9 +5306,9 @@ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+ NbDid |= 1;
+
+ reg = 0x94;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 0, reg);
+ if (!(val & (1 << MemClkFreqVal)))
+- val = Get_NB32(dev, reg + 0x100); /* get the DCT1 value */
++ val = Get_NB32_DCT(dev, 1, reg); /* get the DCT1 value */
+
+ val &= 0x07;
+ val += 3;
+@@ -3426,28 +5377,204 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat)
++ struct DCTStatStruc *pDCTstat, u8 dct)
++{
++ mct_ProgramODT_D(pMCTstat, pDCTstat, dct);
++}
++
++static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u8 i;
+- u32 reg_off, dword;
++ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+
+- if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (is_fam15h()) {
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++ uint32_t odt_pattern_0;
++ uint32_t odt_pattern_1;
++ uint32_t odt_pattern_2;
++ uint32_t odt_pattern_3;
++ uint8_t write_odt_duration;
++ uint8_t read_odt_duration;
++ uint8_t write_odt_delay;
++ uint8_t read_odt_delay;
++
++ /* Select appropriate ODT pattern for installed DIMMs
++ * Refer to the Fam15h BKDG Rev. 3.14, page 149 onwards
++ */
++ if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED]) {
++ if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ if (rank_count_dimm1 == 1) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00020000;
++ } else if (rank_count_dimm1 == 2) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x08020000;
++ } else if (rank_count_dimm1 == 4) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x020a0000;
++ odt_pattern_3 = 0x080a0000;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x08020000;
++ }
++ } else {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x01010202;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x09030603;
++ } else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4)) {
++ odt_pattern_0 = 0x01010000;
++ odt_pattern_1 = 0x01010a0a;
++ odt_pattern_2 = 0x01090000;
++ odt_pattern_3 = 0x01030e0b;
++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4)) {
++ odt_pattern_0 = 0x00000202;
++ odt_pattern_1 = 0x05050202;
++ odt_pattern_2 = 0x00000206;
++ odt_pattern_3 = 0x0d070203;
++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 == 4)) {
++ odt_pattern_0 = 0x05050a0a;
++ odt_pattern_1 = 0x05050a0a;
++ odt_pattern_2 = 0x050d0a0e;
++ odt_pattern_3 = 0x05070a0b;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ }
++ } else {
++ /* FIXME
++ * 3 DIMMs per channel UNIMPLEMENTED
++ */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ } else if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
++ /* TODO
++ * Load reduced dimms UNIMPLEMENTED
++ */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ } else {
++ if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ if (rank_count_dimm1 == 1) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00020000;
++ } else if (rank_count_dimm1 == 2) {
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x08020000;
++ } else {
++ /* Fallback */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x08020000;
++ }
++ } else {
++ /* 2 DIMMs detected */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x01010202;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x09030603;
++ }
++ } else {
++ /* FIXME
++ * 3 DIMMs per channel UNIMPLEMENTED
++ */
++ odt_pattern_0 = 0x00000000;
++ odt_pattern_1 = 0x00000000;
++ odt_pattern_2 = 0x00000000;
++ odt_pattern_3 = 0x00000000;
++ }
++ }
++
++ if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
++ /* TODO
++ * Load reduced dimms UNIMPLEMENTED
++ */
++ write_odt_duration = 0x0;
++ read_odt_duration = 0x0;
++ write_odt_delay = 0x0;
++ read_odt_delay = 0x0;
++ } else {
++ uint8_t tcl;
++ uint8_t tcwl;
++ tcl = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;
++ tcwl = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f;
++
++ write_odt_duration = 0x6;
++ read_odt_duration = 0x6;
++ write_odt_delay = 0x0;
++ if (tcl > tcwl)
++ read_odt_delay = tcl - tcwl;
++ else
++ read_odt_delay = 0x0;
++ }
++
++ /* Program ODT pattern */
++ Set_NB32_DCT(dev, dct, 0x230, odt_pattern_1);
++ Set_NB32_DCT(dev, dct, 0x234, odt_pattern_0);
++ Set_NB32_DCT(dev, dct, 0x238, odt_pattern_3);
++ Set_NB32_DCT(dev, dct, 0x23c, odt_pattern_2);
++ dword = Get_NB32_DCT(dev, dct, 0x240);
++ dword &= ~(0x7 << 12); /* WrOdtOnDuration = write_odt_duration */
++ dword |= (write_odt_duration & 0x7) << 12;
++ dword &= ~(0x7 << 8); /* WrOdtTrnOnDly = write_odt_delay */
++ dword |= (write_odt_delay & 0x7) << 8;
++ dword &= ~(0xf << 4); /* RdOdtOnDuration = read_odt_duration */
++ dword |= (read_odt_duration & 0xf) << 4;
++ dword &= ~(0xf); /* RdOdtTrnOnDly = read_odt_delay */
++ dword |= (read_odt_delay & 0xf);
++ Set_NB32_DCT(dev, dct, 0x240, dword);
++ } else if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+ if (pDCTstat->Speed == 3)
+ dword = 0x00000800;
+ else
+ dword = 0x00000000;
+ for (i=0; i < 2; i++) {
+- reg_off = 0x100 * i;
+- Set_NB32(dev, 0x98 + reg_off, 0x0D000030);
+- Set_NB32(dev, 0x9C + reg_off, dword);
+- Set_NB32(dev, 0x98 + reg_off, 0x4D040F30);
+-
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ Set_NB32_DCT(dev, i, 0x98, 0x0D000030);
++ Set_NB32_DCT(dev, i, 0x9C, dword);
++ Set_NB32_DCT(dev, i, 0x98, 0x4D040F30);
+
+ /* Obtain number of DIMMs on channel */
+ uint8_t dimm_count = pDCTstat->MAdimms[i];
+@@ -3459,7 +5586,7 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ uint32_t odt_pattern_3;
+
+ /* Select appropriate ODT pattern for installed DIMMs
+- * Refer to the BKDG Rev. 3.62, page 120 onwards
++ * Refer to the Fam10h BKDG Rev. 3.62, page 120 onwards
+ */
+ if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) {
+ if (MaxDimmsInstallable == 2) {
+@@ -3570,10 +5697,10 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ /* Program ODT pattern */
+- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x180, odt_pattern_1);
+- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x181, odt_pattern_0);
+- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x182, odt_pattern_3);
+- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x183, odt_pattern_2);
++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x180, odt_pattern_1);
++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x181, odt_pattern_0);
++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x182, odt_pattern_3);
++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x183, odt_pattern_2);
+ }
+ }
+ }
+@@ -3581,34 +5708,32 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct, val;
+
+ /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */
+ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) {
+- Set_NB32(dev, 0x9C + reg_off, 0x1C);
+- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006);
+- Set_NB32(dev, 0x9C + reg_off, 0x13D);
+- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007);
++ Set_NB32_DCT(dev, dct, 0x9C, 0x1C);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006);
++ Set_NB32_DCT(dev, dct, 0x9C, 0x13D);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007);
+
+- val = Get_NB32(dev, 0x90 + reg_off);
++ val = Get_NB32_DCT(dev, dct, 0x90);
+ val &= ~(1 << 27/* DisDllShutdownSR */);
+- Set_NB32(dev, 0x90 + reg_off, val);
++ Set_NB32_DCT(dev, dct, 0x90, val);
+ }
+ }
+
+ static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+
+ /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */
+ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) {
+- Set_NB32(dev, 0x9C + reg_off, 0x7D0);
+- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006);
+- Set_NB32(dev, 0x9C + reg_off, 0x190);
+- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007);
++ Set_NB32_DCT(dev, dct, 0x9C, 0x7D0);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006);
++ Set_NB32_DCT(dev, dct, 0x9C, 0x190);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007);
+
+ DramConfigLo |= /* DisDllShutdownSR */ 1 << 27;
+ }
+@@ -3700,52 +5825,61 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ DramMRS |= 1 << 23;
+ }
+ }
+- /*
+- DRAM MRS Register
+- DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7)
+- */
+- DramMRS |= 1 << 2;
+- /* Dram nominal termination: */
+- byte = pDCTstat->MAdimms[dct];
+- if (!(pDCTstat->Status & (1 << SB_Registered))) {
+- DramMRS |= 1 << 7; /* 60 ohms */
+- if (byte & 2) {
+- if (pDCTstat->Speed < 6)
+- DramMRS |= 1 << 8; /* 40 ohms */
+- else
+- DramMRS |= 1 << 9; /* 30 ohms */
++
++ if (is_fam15h()) {
++ DramMRS |= (0x1 << 23); /* PchgPDModeSel = 1 */
++ } else {
++ /*
++ DRAM MRS Register
++ DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7)
++ */
++ DramMRS |= 1 << 2;
++ /* Dram nominal termination: */
++ byte = pDCTstat->MAdimms[dct];
++ if (!(pDCTstat->Status & (1 << SB_Registered))) {
++ DramMRS |= 1 << 7; /* 60 ohms */
++ if (byte & 2) {
++ if (pDCTstat->Speed < 6)
++ DramMRS |= 1 << 8; /* 40 ohms */
++ else
++ DramMRS |= 1 << 9; /* 30 ohms */
++ }
+ }
+- }
+- /* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */
+- if (!(pDCTstat->Status & (1 << SB_Registered))) {
+- if (byte >= 2) {
+- if (pDCTstat->Speed == 7)
+- DramMRS |= 1 << 10;
+- else
+- DramMRS |= 1 << 11;
++ /* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */
++ if (!(pDCTstat->Status & (1 << SB_Registered))) {
++ if (byte >= 2) {
++ if (pDCTstat->Speed == 7)
++ DramMRS |= 1 << 10;
++ else
++ DramMRS |= 1 << 11;
++ }
++ } else {
++ DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte);
+ }
+- } else {
+- DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte);
++
++ /* Qoff=0, output buffers enabled */
++ /* Tcwl */
++ DramMRS |= (pDCTstat->Speed - 4) << 20;
++ /* ASR=1, auto self refresh */
++ /* SRT=0 */
++ DramMRS |= 1 << 18;
+ }
+
+ /* burst length control */
+ if (pDCTstat->Status & (1 << SB_128bitmode))
+ DramMRS |= 1 << 1;
+- /* Qoff=0, output buffers enabled */
+- /* Tcwl */
+- DramMRS |= (pDCTstat->Speed - 4) << 20;
+- /* ASR=1, auto self refresh */
+- /* SRT=0 */
+- DramMRS |= 1 << 18;
+-
+- dword = Get_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84);
+- dword &= ~0x00FC2F8F;
++
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
++ if (is_fam15h())
++ dword &= ~0x00800003;
++ else
++ dword &= ~0x00fc2f8f;
+ dword |= DramMRS;
+- Set_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84, dword);
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
+ }
+
+-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct,
+- u32 DramConfigHi)
++void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi)
+ {
+ /* Bug#15114: Comp. update interrupted by Freq. change can cause
+ * subsequent update to be invalid during any MemClk frequency change:
+@@ -3774,45 +5908,86 @@ void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct,
+ */
+
+ u32 dev = pDCTstat->dev_dct;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u32 index;
+
+- u32 val;
++ uint32_t dword;
+
+- index = 0x08;
+- val = Get_NB32_index_wait(dev, index_reg, index);
+- if (!(val & (1 << DisAutoComp)))
+- Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp));
++ if (is_fam15h()) {
++ /* Initial setup for frequency change
++ * 9C_x0000_0004 must be configured before MemClkFreqVal is set
++ */
+
+- mct_Wait(100);
++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x00000190;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
+
+- Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi);
++ dword = Get_NB32_DCT(dev, dct, 0x94);
++ dword &= ~(1 << MemClkFreqVal);
++ Set_NB32_DCT(dev, dct, 0x94, dword);
++
++ dword = DramConfigHi;
++ dword &= ~(1 << MemClkFreqVal);
++ Set_NB32_DCT(dev, dct, 0x94, dword);
++
++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
++ set_2t_configuration(pMCTstat, pDCTstat, dct);
++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
++ mct_PlatformSpec(pMCTstat, pDCTstat, dct);
++ } else {
++ index = 0x08;
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++ if (!(dword & (1 << DisAutoComp)))
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, dword | (1 << DisAutoComp));
++
++ mct_Wait(100);
++ }
++
++ /* Program the DRAM Configuration High register */
++ Set_NB32_DCT(dev, dct, 0x94, DramConfigHi);
++
++ if (is_fam15h()) {
++ /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
++ do {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
++ } while (dword & (1 << FreqChgInProg));
++
++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x0000000f;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
++ }
+ }
+
+ static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+- u8 Node;
+- struct DCTStatStruc *pDCTstat;
+-
+- /* Errata 178
+- *
+- * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations
+- * In TX FIFO
+- * Solution: BIOS should program DRAM Control Register[RdPtrInit] =
+- * 5h, (F2x[1, 0]78[3:0] = 5h).
+- * Silicon Status: Fixed In Rev B0
+- *
+- * Bug#15880: Determine validity of reset settings for DDR PHY timing.
+- * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training.
+- */
+- for (Node = 0; Node < 8; Node++) {
+- pDCTstat = pDCTstatA + Node;
++ if (!is_fam15h()) {
++ u8 Node;
++ struct DCTStatStruc *pDCTstat;
+
+- if (pDCTstat->NodePresent) {
+- mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */
+- mct_ResetDLL_D(pMCTstat, pDCTstat, 0);
+- mct_ResetDLL_D(pMCTstat, pDCTstat, 1);
++ /* Errata 178
++ *
++ * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations
++ * In TX FIFO
++ * Solution: BIOS should program DRAM Control Register[RdPtrInit] =
++ * 5h, (F2x[1, 0]78[3:0] = 5h).
++ * Silicon Status: Fixed In Rev B0
++ *
++ * Bug#15880: Determine validity of reset settings for DDR PHY timing.
++ * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training.
++ */
++ for (Node = 0; Node < 8; Node++) {
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent) {
++ mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */
++ mct_ResetDLL_D(pMCTstat, pDCTstat, 0);
++ mct_ResetDLL_D(pMCTstat, pDCTstat, 1);
++ }
+ }
+ }
+ }
+@@ -3823,7 +5998,6 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ {
+ u8 Receiver;
+ u32 dev = pDCTstat->dev_dct;
+- u32 reg_off = 0x100 * dct;
+ u32 addr;
+ u32 lo, hi;
+ u8 wrap32dis = 0;
+@@ -3834,6 +6008,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ return;
+ }
+
++ /* Skip reset DLL for Family 15h */
++ if (is_fam15h()) {
++ return;
++ }
++
+ addr = HWCR;
+ _RDMSR(addr, &lo, &hi);
+ if(lo & (1<<17)) { /* save the old value */
+@@ -3853,11 +6032,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr); /* cache fills */
+
+ /* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */
+- Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00008000);
++ Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00008000);
+ mct_Wait(80); /* wait >= 300ns */
+
+ /* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
+- Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00000000);
++ Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00000000);
+ mct_Wait(800); /* wait >= 2us */
+ break;
+ }
+@@ -3897,39 +6076,40 @@ static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat,
+ static void SetDllSpeedUp_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 val;
+- u32 dev = pDCTstat->dev_dct;
+- u32 reg_off = 0x100 * dct;
+-
+- if (pDCTstat->Speed >= 7) { /* DDR1600 and above */
+- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */
+- Set_NB32(dev, reg_off + 0x98, 0x0D080F10);
+- val = Get_NB32(dev, reg_off + 0x9C);
+- val |= 1 < 13;
+- Set_NB32(dev, reg_off + 0x9C, val);
+- Set_NB32(dev, reg_off + 0x98, 0x4D080F10);
+-
+- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */
+- Set_NB32(dev, reg_off + 0x98, 0x0D080F11);
+- val = Get_NB32(dev, reg_off + 0x9C);
+- val |= 1 < 13;
+- Set_NB32(dev, reg_off + 0x9C, val);
+- Set_NB32(dev, reg_off + 0x98, 0x4D080F11);
+-
+- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */
+- Set_NB32(dev, reg_off + 0x98, 0x0D088F30);
+- val = Get_NB32(dev, reg_off + 0x9C);
+- val |= 1 < 13;
+- Set_NB32(dev, reg_off + 0x9C, val);
+- Set_NB32(dev, reg_off + 0x98, 0x4D088F30);
+-
+- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */
+- Set_NB32(dev, reg_off + 0x98, 0x0D08CF30);
+- val = Get_NB32(dev, reg_off + 0x9C);
+- val |= 1 < 13;
+- Set_NB32(dev, reg_off + 0x9C, val);
+- Set_NB32(dev, reg_off + 0x98, 0x4D08CF30);
+-
++ if (!is_fam15h()) {
++ u32 val;
++ u32 dev = pDCTstat->dev_dct;
++
++ if (pDCTstat->Speed >= mhz_to_memclk_config(800)) { /* DDR1600 and above */
++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */
++ Set_NB32_DCT(dev, dct, 0x98, 0x0D080F10);
++ val = Get_NB32_DCT(dev, dct, 0x9C);
++ val |= 1 < 13;
++ Set_NB32_DCT(dev, dct, 0x9C, val);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D080F10);
++
++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */
++ Set_NB32_DCT(dev, dct, 0x98, 0x0D080F11);
++ val = Get_NB32_DCT(dev, dct, 0x9C);
++ val |= 1 < 13;
++ Set_NB32_DCT(dev, dct, 0x9C, val);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D080F11);
++
++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */
++ Set_NB32_DCT(dev, dct, 0x98, 0x0D088F30);
++ val = Get_NB32_DCT(dev, dct, 0x9C);
++ val |= 1 < 13;
++ Set_NB32_DCT(dev, dct, 0x9C, val);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D088F30);
++
++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */
++ Set_NB32_DCT(dev, dct, 0x98, 0x0D08CF30);
++ val = Get_NB32_DCT(dev, dct, 0x9C);
++ val |= 1 < 13;
++ Set_NB32_DCT(dev, dct, 0x9C, val);
++ Set_NB32_DCT(dev, dct, 0x98, 0x4D08CF30);
++
++ }
+ }
+ }
+
+@@ -3957,7 +6137,6 @@ static void SyncSetting(struct DCTStatStruc *pDCTstat)
+ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
+
+ u32 val;
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_B2 | AMD_DR_B3)) {
+@@ -3965,16 +6144,16 @@ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
+ val = Get_NB32(dev, 0x110);
+ if (!(val & (1 << DramEnabled))) {
+ /* If 50 us expires while DramEnable =0 then do the following */
+- val = Get_NB32(dev, 0x90 + reg_off);
++ val = Get_NB32_DCT(dev, dct, 0x90);
+ val &= ~(1 << Width128); /* Program Width128 = 0 */
+- Set_NB32(dev, 0x90 + reg_off, val);
++ Set_NB32_DCT(dev, dct, 0x90, val);
+
+- val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05); /* Perform dummy CSR read to F2x09C_x05 */
++ val = Get_NB32_index_wait_DCT(dev, dct, 0x98, 0x05); /* Perform dummy CSR read to F2x09C_x05 */
+
+ if (pDCTstat->GangedMode) {
+- val = Get_NB32(dev, 0x90 + reg_off);
++ val = Get_NB32_DCT(dev, dct, 0x90);
+ val |= 1 << Width128; /* Program Width128 = 0 */
+- Set_NB32(dev, 0x90 + reg_off, val);
++ Set_NB32_DCT(dev, dct, 0x90, val);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index a947c2d..50fbff7 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -76,6 +76,8 @@
+ /* #define PA_EXT_DCTADDL (((00 << 3)+5) << 8) */ /*Node x DCT function, Additional Registers PCI Address bits [15:0]*/
+
+ #define PA_NBMISC(Node) ((((0x18+Node) << 3)+3) << 12) /*Node 0 Misc PCI Address bits [15:0]*/
++#define PA_LINK(Node) ((((0x18+Node) << 3)+4) << 12) /*Node 0 Link Control bits [15:0]*/
++#define PA_NBCTL(Node) ((((0x18+Node) << 3)+5) << 12) /*Node 0 NB Control PCI Address bits [15:0]*/
+ /* #define PA_NBDEVOP (((00 << 3)+3) << 8) */ /*Node 0 Misc PCI Address bits [15:0]*/
+
+ #define DCC_EN 1 /* X:2:0x94[19]*/
+@@ -129,7 +131,7 @@
+ #define X4Dimm 12 /* func 2, offset 90h, bit 12*/
+ #define UnBuffDimm 16 /* func 2, offset 90h, bit 16*/
+ #define DimmEcEn 19 /* func 2, offset 90h, bit 19*/
+-#define MemClkFreqVal 3 /* func 2, offset 94h, bit 3*/
++#define MemClkFreqVal ((is_fam15h())?7:3) /* func 2, offset 94h, bit 3 or 7*/
+ #define RDqsEn 12 /* func 2, offset 94h, bit 12*/
+ #define DisDramInterface 14 /* func 2, offset 94h, bit 14*/
+ #define PowerDownEn 15 /* func 2, offset 94h, bit 15*/
+@@ -204,6 +206,7 @@
+ #define JED_PROBEMSK 0x40 /*Analysis Probe installed*/
+ #define JED_RDIMM 0x1 /* RDIMM */
+ #define JED_MiniRDIMM 0x5 /* Mini-RDIMM */
++ #define JED_LRDIMM 0xb /* Load-reduced DIMM */
+ #define SPD_Density 4 /* Bank address bits,SDRAM capacity */
+ #define SPD_Addressing 5 /* Row/Column address bits */
+ #define SPD_Voltage 6 /* Supported voltage bitfield */
+@@ -297,6 +300,7 @@ struct MCTStatStruc {
+ of sub 4GB dram hole for HW remapping.*/
+ u32 Sub4GCacheTop; /* If not zero, the 32-bit top of cacheable memory.*/
+ u32 SysLimit; /* LIMIT[39:8] (system address)*/
++ uint32_t TSCFreq;
+ } __attribute__((packed));
+
+ /*=============================================================================
+@@ -320,7 +324,8 @@ struct MCTStatStruc {
+
+ struct DCTStatStruc { /* A per Node structure*/
+ /* DCTStatStruct_F - start */
+- u8 Node_ID; /* Node ID of current controller*/
++ u8 Node_ID; /* Node ID of current controller */
++ uint8_t stopDCT; /* Set if the DCT will be stopped */
+ u8 ErrCode; /* Current error condition of Node
+ 0= no error
+ 1= Variance Error, DCT is running but not in an optimal configuration.
+@@ -464,7 +469,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ /* CH A byte lane 0 - 7 maximum filtered window passing DQS delay value*/
+ /* CH B byte lane 0 - 7 minimum filtered window passing DQS delay value*/
+ /* CH B byte lane 0 - 7 maximum filtered window passing DQS delay value*/
+- u32 LogicalCPUID; /* The logical CPUID of the node*/
++ uint64_t LogicalCPUID; /* The logical CPUID of the node*/
+ u16 HostBiosSrvc1; /* Word sized general purpose field for use by host BIOS. Scratch space.*/
+ u32 HostBiosSrvc2; /* Dword sized general purpose field for use by host BIOS. Scratch space.*/
+ u16 DimmQRPresent; /* QuadRank DIMM present?*/
+@@ -558,12 +563,20 @@ struct DCTStatStruc { /* A per Node structure*/
+ u8 ClToNB_flag; /* is used to restore ClLinesToNbDis bit after memory */
+ u32 NodeSysBase; /* for channel interleave usage */
+
++ /* Fam15h specific backup variables */
++ uint8_t SwNbPstateLoDis;
++ uint8_t NbPstateDisOnP0;
++ uint8_t NbPstateThreshold;
++ uint8_t NbPstateHi;
++
+ /* New for LB Support */
+ u8 NodePresent;
+ u32 dev_host;
+ u32 dev_map;
+ u32 dev_dct;
+ u32 dev_nbmisc;
++ u32 dev_link;
++ u32 dev_nbctl;
+ u8 TargetFreq;
+ u8 TargetCASL;
+ u8 CtrlWrd3;
+@@ -596,9 +609,10 @@ struct DCTStatStruc { /* A per Node structure*/
+ uint8_t DimmBanks[MAX_DIMMS_SUPPORTED];
+ uint8_t DimmWidth[MAX_DIMMS_SUPPORTED];
+ uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED];
++ uint8_t DimmLoadReduced[MAX_DIMMS_SUPPORTED];
+
+ uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED];
+- char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH];
++ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
+ uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
+ uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
+ } __attribute__((packed));
+@@ -701,7 +715,64 @@ struct amd_s3_persistent_mct_channel_data {
+ /* Other (1 dword) */
+ uint32_t f3x58;
+
+- /* TOTAL: 250 dwords */
++ /* Family 15h-specific registers (90 dwords) */
++ uint32_t f2x200;
++ uint32_t f2x204;
++ uint32_t f2x208;
++ uint32_t f2x20c;
++ uint32_t f2x210[4]; /* [nb pstate] */
++ uint32_t f2x214;
++ uint32_t f2x218;
++ uint32_t f2x21c;
++ uint32_t f2x22c;
++ uint32_t f2x230;
++ uint32_t f2x234;
++ uint32_t f2x238;
++ uint32_t f2x23c;
++ uint32_t f2x240;
++ uint32_t f2x9cx0d0fe003;
++ uint32_t f2x9cx0d0fe013;
++ uint32_t f2x9cx0d0f0_8_0_1f[9]; /* [lane]*/
++ uint32_t f2x9cx0d0f201f;
++ uint32_t f2x9cx0d0f211f;
++ uint32_t f2x9cx0d0f221f;
++ uint32_t f2x9cx0d0f801f;
++ uint32_t f2x9cx0d0f811f;
++ uint32_t f2x9cx0d0f821f;
++ uint32_t f2x9cx0d0fc01f;
++ uint32_t f2x9cx0d0fc11f;
++ uint32_t f2x9cx0d0fc21f;
++ uint32_t f2x9cx0d0f4009;
++ uint32_t f2x9cx0d0f0_8_0_02[9]; /* [lane]*/
++ uint32_t f2x9cx0d0f0_8_0_06[9]; /* [lane]*/
++ uint32_t f2x9cx0d0f0_8_0_0a[9]; /* [lane]*/
++ uint32_t f2x9cx0d0f2002;
++ uint32_t f2x9cx0d0f2102;
++ uint32_t f2x9cx0d0f2202;
++ uint32_t f2x9cx0d0f8002;
++ uint32_t f2x9cx0d0f8006;
++ uint32_t f2x9cx0d0f800a;
++ uint32_t f2x9cx0d0f8102;
++ uint32_t f2x9cx0d0f8106;
++ uint32_t f2x9cx0d0f810a;
++ uint32_t f2x9cx0d0fc002;
++ uint32_t f2x9cx0d0fc006;
++ uint32_t f2x9cx0d0fc00a;
++ uint32_t f2x9cx0d0fc00e;
++ uint32_t f2x9cx0d0fc012;
++ uint32_t f2x9cx0d0f2031;
++ uint32_t f2x9cx0d0f2131;
++ uint32_t f2x9cx0d0f2231;
++ uint32_t f2x9cx0d0f8031;
++ uint32_t f2x9cx0d0f8131;
++ uint32_t f2x9cx0d0f8231;
++ uint32_t f2x9cx0d0fc031;
++ uint32_t f2x9cx0d0fc131;
++ uint32_t f2x9cx0d0fc231;
++ uint32_t f2x9cx0d0f0_0_f_31[9]; /* [lane] */
++ uint32_t f2x9cx0d0f8021;
++
++ /* TOTAL: 340 dwords */
+ } __attribute__((packed));
+
+ struct amd_s3_persistent_node_data {
+@@ -746,18 +817,19 @@ struct amd_s3_persistent_data {
+ Local Configuration Status (DCTStatStruc.Status[31:0])
+ ===============================================================================*/
+ #define SB_Registered 0 /* All DIMMs are Registered*/
+-#define SB_ECCDIMMs 1 /* All banks ECC capable*/
+-#define SB_PARDIMMs 2 /* All banks Addr/CMD Parity capable*/
+-#define SB_DiagClks 3 /* Jedec ALL slots clock enable diag mode*/
+-#define SB_128bitmode 4 /* DCT in 128-bit mode operation*/
+-#define SB_64MuxedMode 5 /* DCT in 64-bit mux'ed mode.*/
+-#define SB_2TMode 6 /* 2T CMD timing mode is enabled.*/
+-#define SB_SWNodeHole 7 /* Remapping of Node Base on this Node to create a gap.*/
+-#define SB_HWHole 8 /* Memory Hole created on this Node using HW remapping.*/
+-#define SB_Over400MHz 9 /* DCT freq >= 400MHz flag*/
+-#define SB_DQSPos_Pass2 10 /* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/
+-#define SB_DQSRcvLimit 11 /* Using for DQSRcvEnTrain to know we have reached to upper bound.*/
+-#define SB_ExtConfig 12 /* Indicator the default setting for extend PCI configuration support*/
++#define SB_LoadReduced 1 /* All DIMMs are Load-Reduced*/
++#define SB_ECCDIMMs 2 /* All banks ECC capable*/
++#define SB_PARDIMMs 3 /* All banks Addr/CMD Parity capable*/
++#define SB_DiagClks 4 /* Jedec ALL slots clock enable diag mode*/
++#define SB_128bitmode 5 /* DCT in 128-bit mode operation*/
++#define SB_64MuxedMode 6 /* DCT in 64-bit mux'ed mode.*/
++#define SB_2TMode 7 /* 2T CMD timing mode is enabled.*/
++#define SB_SWNodeHole 8 /* Remapping of Node Base on this Node to create a gap.*/
++#define SB_HWHole 9 /* Memory Hole created on this Node using HW remapping.*/
++#define SB_Over400MHz 10 /* DCT freq >= 400MHz flag*/
++#define SB_DQSPos_Pass2 11 /* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/
++#define SB_DQSRcvLimit 12 /* Using for DQSRcvEnTrain to know we have reached to upper bound.*/
++#define SB_ExtConfig 13 /* Indicator the default setting for extend PCI configuration support*/
+
+
+ /*===============================================================================
+@@ -775,17 +847,18 @@ struct amd_s3_persistent_data {
+ 266=266MHz (DDR533)
+ 333=333MHz (DDR667)
+ 400=400MHz (DDR800)*/
+-#define NV_ECC_CAP 4 /* Bus ECC capable (1-bits)
++#define NV_MIN_MEMCLK 4 /* Minimum platform demonstrated Memclock (10-bits) */
++#define NV_ECC_CAP 5 /* Bus ECC capable (1-bits)
+ 0=Platform not capable
+ 1=Platform is capable*/
+-#define NV_4RANKType 5 /* Quad Rank DIMM slot type (2-bits)
++#define NV_4RANKType 6 /* Quad Rank DIMM slot type (2-bits)
+ 0=Normal
+ 1=R4 (4-Rank Registered DIMMs in AMD server configuration)
+ 2=S4 (Unbuffered SO-DIMMs)*/
+-#define NV_BYPMAX 6 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
++#define NV_BYPMAX 7 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
+ 4=4 times bypass (normal for non-UMA systems)
+ 7=7 times bypass (normal for UMA systems)*/
+-#define NV_RDWRQBYP 7 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
++#define NV_RDWRQBYP 8 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
+ 2=8 times (normal for non-UMA systems)
+ 3=16 times (normal for UMA systems)*/
+
+@@ -848,8 +921,9 @@ struct amd_s3_persistent_data {
+ #define NV_ECCRedir 54 /* Dram ECC Redirection enable*/
+ #define NV_DramBKScrub 55 /* Dram ECC Background Scrubber CTL*/
+ #define NV_L2BKScrub 56 /* L2 ECC Background Scrubber CTL*/
+-#define NV_DCBKScrub 57 /* DCache ECC Background Scrubber CTL*/
+-#define NV_CS_SpareCTL 58 /* Chip Select Spare Control bit 0:
++#define NV_L3BKScrub 57 /* L3 ECC Background Scrubber CTL*/
++#define NV_DCBKScrub 58 /* DCache ECC Background Scrubber CTL*/
++#define NV_CS_SpareCTL 59 /* Chip Select Spare Control bit 0:
+ 0=disable Spare
+ 1=enable Spare */
+ /* Chip Select Spare Control bit 1-4:
+@@ -900,10 +974,12 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u8 FinalVa
+ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel);
+ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct);
+ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi);
++void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi);
+ void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+ void mct_SetClToNB_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ void mct_SetWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
++void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
++void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 Pass);
+ void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 _DisableDramECC);
+ u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+index c40ea1a..f6aa755 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+@@ -98,6 +98,15 @@ static u32 bsf(u32 x)
+
+ u32 SetUpperFSbase(u32 addr_hi);
+
++static void proc_MFENCE(void)
++{
++ __asm__ volatile (
++ "outb %%al, $0xed\n\t" /* _EXECFENCE */
++ "mfence\n\t"
++ :::"memory"
++ );
++}
++
+ static void proc_CLFLUSH(u32 addr_hi)
+ {
+ SetUpperFSbase(addr_hi);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
+index 126642b..3df262b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -17,6 +18,8 @@
+ * Foundation, Inc.
+ */
+
++/* AM3/ASB2/C32/G34 DDR3 */
++
+ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+ u32 *AddrTmgCTL, u32 *ODC_CTL,
+ u8 *CMDmode);
+@@ -24,17 +27,23 @@ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 dct)
+ {
+- Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed,
+- pDCTstat->MAload[dct],
+- &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
+- &pDCTstat->_2Tmode);
++ if (is_fam15h()) {
++ pDCTstat->CH_ADDR_TMG[dct] = fam15h_address_timing_compensation_code(pDCTstat, dct);
++ pDCTstat->CH_ODC_CTL[dct] = fam15h_output_driver_compensation_code(pDCTstat, dct);
++ pDCTstat->_2Tmode = fam15h_slow_access_mode(pDCTstat, dct);
++ } else {
++ Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed,
++ pDCTstat->MAload[dct],
++ &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
++ &pDCTstat->_2Tmode);
++
++ pDCTstat->CH_ODC_CTL[dct] |= 0x20000000; /* 60ohms */
++ }
+
+ pDCTstat->CH_EccDQSLike[0] = 0x0403;
+ pDCTstat->CH_EccDQSScale[0] = 0x70;
+ pDCTstat->CH_EccDQSLike[1] = 0x0403;
+ pDCTstat->CH_EccDQSScale[1] = 0x70;
+-
+- pDCTstat->CH_ODC_CTL[dct] |= 0x20000000; /* 60ohms */
+ }
+
+ /*
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
+index f1fd7a5..a1cdfa6 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -35,7 +36,6 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+
+ u32 dev;
+ u32 reg;
+- u32 reg_off;
+ u32 val;
+ u32 val_lo, val_hi;
+
+@@ -44,16 +44,15 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ EnChipSels = 0;
+
+ dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * dct;
+
+ ChipSel = 0; /* Find out if current configuration is capable */
+ while (DoIntlv && (ChipSel < MAX_CS_SUPPORTED)) {
+- reg = 0x40+(ChipSel<<2) + reg_off; /* Dram CS Base 0 */
+- val = Get_NB32(dev, reg);
++ reg = 0x40+(ChipSel<<2); /* Dram CS Base 0 */
++ val = Get_NB32_DCT(dev, dct, reg);
+ if ( val & (1<<CSEnable)) {
+ EnChipSels++;
+- reg = 0x60+((ChipSel>>1)<<2)+reg_off; /*Dram CS Mask 0 */
+- val = Get_NB32(dev, reg);
++ reg = 0x60+((ChipSel>>1)<<2); /*Dram CS Mask 0 */
++ val = Get_NB32_DCT(dev, dct, reg);
+ val >>= 19;
+ val &= 0x3ff;
+ val++;
+@@ -63,8 +62,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ /*If mask sizes not same then skip */
+ if (val != MemSize)
+ break;
+- reg = 0x80 + reg_off; /*Dram Bank Addressing */
+- val = Get_NB32(dev, reg);
++ reg = 0x80; /*Dram Bank Addressing */
++ val = Get_NB32_DCT(dev, dct, reg);
+ val >>= (ChipSel>>1)<<2;
+ val &= 0x0f;
+ if(EnChipSels == 1)
+@@ -103,8 +102,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ BitDelta = bsf(AddrHiMask) - bsf(AddrLoMask);
+
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel++) {
+- reg = 0x40+(ChipSel<<2) + reg_off; /*Dram CS Base 0 */
+- val = Get_NB32(dev, reg);
++ reg = 0x40+(ChipSel<<2); /*Dram CS Base 0 */
++ val = Get_NB32_DCT(dev, dct, reg);
+ if (val & 3) {
+ val_lo = val & AddrLoMask;
+ val_hi = val & AddrHiMask;
+@@ -114,13 +113,13 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ val_hi >>= BitDelta;
+ val |= val_lo;
+ val |= val_hi;
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+
+ if(ChipSel & 1)
+ continue;
+
+- reg = 0x60 + ((ChipSel>>1)<<2) + reg_off; /*Dram CS Mask 0 */
+- val = Get_NB32(dev, reg);
++ reg = 0x60 + ((ChipSel>>1)<<2); /*Dram CS Mask 0 */
++ val = Get_NB32_DCT(dev, dct, reg);
+ val_lo = val & AddrLoMask;
+ val_hi = val & AddrHiMask;
+ val &= AddrLoMaskN;
+@@ -129,7 +128,7 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ val_hi >>= BitDelta;
+ val |= val_lo;
+ val |= val_hi;
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ }
+ }
+ } /* DoIntlv */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 8572243..e74545b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -18,6 +18,12 @@
+ * Foundation, Inc.
+ */
+
++static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
++
++static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay,
++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
++
+ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u16 like,
+ u8 scale, u8 ChipSel);
+@@ -37,7 +43,7 @@ static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 addr_lo);
+ static void SetTargetWTIO_D(u32 TestAddr);
+ static void ResetTargetWTIO_D(void);
+-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index);
++void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index);
+ u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+@@ -54,6 +60,7 @@ static void proc_IOCLFLUSH_D(u32 addr_hi);
+ static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel);
+
+ #define DQS_TRAIN_DEBUG 0
++// #define PRINT_PASS_FAIL_BITMAPS 1
+
+ static void print_debug_dqs(const char *str, u32 val, u8 level)
+ {
+@@ -198,18 +205,20 @@ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->DCTSysLimit) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x78);
+- val |= 1 <<DqsRcvEnTrain;
+- Set_NB32(pDCTstat->dev_dct, 0x78, val);
+- val = Get_NB32(pDCTstat->dev_dct, 0x78 + 0x100);
+- val |= 1 <<DqsRcvEnTrain;
+- Set_NB32(pDCTstat->dev_dct, 0x78 + 0x100, val);
++ if (!is_fam15h()) {
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x78);
++ val |= 1 <<DqsRcvEnTrain;
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x78, val);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x78);
++ val |= 1 <<DqsRcvEnTrain;
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x78, val);
++ }
+ mct_TrainRcvrEn_D(pMCTstat, pDCTstat, Pass);
+ }
+ }
+ }
+
+-static void SetEccDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
++static void SetEccDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+ {
+ u8 channel;
+@@ -268,68 +277,150 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DQSDelay = (u8)DQSDelay;
+ }
+
+-static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++static void read_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint32_t dword;
++ uint32_t mask;
++
++ if (is_fam15h())
++ mask = 0xff;
++ else
++ mask = 0x7f;
++
++ /* Lanes 0 - 3 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8));
++ delay[3] = (dword >> 24) & mask;
++ delay[2] = (dword >> 16) & mask;
++ delay[1] = (dword >> 8) & mask;
++ delay[0] = dword & mask;
++
++ /* Lanes 4 - 7 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8));
++ delay[7] = (dword >> 24) & mask;
++ delay[6] = (dword >> 16) & mask;
++ delay[5] = (dword >> 8) & mask;
++ delay[4] = dword & mask;
++
++ /* Lane 8 (ECC) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8));
++ delay[8] = dword & mask;
++}
++
++static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+ {
+ uint32_t dword;
++ uint32_t mask;
++
++ if (is_fam15h())
++ mask = 0xff;
++ else
++ mask = 0x7f;
+
+ /* Lanes 0 - 3 */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8));
+- dword &= ~0x7f7f7f7f;
+- dword |= (delay[3] & 0x7f) << 24;
+- dword |= (delay[2] & 0x7f) << 16;
+- dword |= (delay[1] & 0x7f) << 8;
+- dword |= delay[0] & 0x7f;
+- Set_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8));
++ dword &= ~(mask << 24);
++ dword &= ~(mask << 16);
++ dword &= ~(mask << 8);
++ dword &= ~mask;
++ dword |= (delay[3] & mask) << 24;
++ dword |= (delay[2] & mask) << 16;
++ dword |= (delay[1] & mask) << 8;
++ dword |= delay[0] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8), dword);
+
+ /* Lanes 4 - 7 */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8));
+- dword &= ~0x7f7f7f7f;
+- dword |= (delay[7] & 0x7f) << 24;
+- dword |= (delay[6] & 0x7f) << 16;
+- dword |= (delay[5] & 0x7f) << 8;
+- dword |= delay[4] & 0x7f;
+- Set_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8));
++ dword &= ~(mask << 24);
++ dword &= ~(mask << 16);
++ dword &= ~(mask << 8);
++ dword &= ~mask;
++ dword |= (delay[7] & mask) << 24;
++ dword |= (delay[6] & mask) << 16;
++ dword |= (delay[5] & mask) << 8;
++ dword |= delay[4] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8), dword);
+
+ /* Lane 8 (ECC) */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8));
+- dword &= ~0x0000007f;
+- dword |= delay[8] & 0x7f;
+- Set_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8));
++ dword &= ~mask;
++ dword |= delay[8] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8), dword);
+ }
+
+-static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++static void read_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+ {
+ uint32_t dword;
++ uint32_t mask;
++
++ if (is_fam15h())
++ mask = 0x3e;
++ else
++ mask = 0x3f;
+
+ /* Lanes 0 - 3 */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8));
+- dword &= ~0x3f3f3f3f;
+- dword |= (delay[3] & 0x3f) << 24;
+- dword |= (delay[2] & 0x3f) << 16;
+- dword |= (delay[1] & 0x3f) << 8;
+- dword |= delay[0] & 0x3f;
+- Set_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8));
++ delay[3] = (dword >> 24) & mask;
++ delay[2] = (dword >> 16) & mask;
++ delay[1] = (dword >> 8) & mask;
++ delay[0] = dword & mask;
+
+ /* Lanes 4 - 7 */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8));
+- dword &= ~0x3f3f3f3f;
+- dword |= (delay[7] & 0x3f) << 24;
+- dword |= (delay[6] & 0x3f) << 16;
+- dword |= (delay[5] & 0x3f) << 8;
+- dword |= delay[4] & 0x3f;
+- Set_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8));
++ delay[7] = (dword >> 24) & mask;
++ delay[6] = (dword >> 16) & mask;
++ delay[5] = (dword >> 8) & mask;
++ delay[4] = dword & mask;
+
+ /* Lane 8 (ECC) */
+- dword = Get_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8));
+- dword &= ~0x0000003f;
+- dword |= delay[8] & 0x3f;
+- Set_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8), dword);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8));
++ delay[8] = dword & mask;
++}
++
++static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint32_t dword;
++ uint32_t mask;
++
++ if (is_fam15h())
++ mask = 0x3e;
++ else
++ mask = 0x3f;
++
++ /* Lanes 0 - 3 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8));
++ dword &= ~(mask << 24);
++ dword &= ~(mask << 16);
++ dword &= ~(mask << 8);
++ dword &= ~mask;
++ dword |= (delay[3] & mask) << 24;
++ dword |= (delay[2] & mask) << 16;
++ dword |= (delay[1] & mask) << 8;
++ dword |= delay[0] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8), dword);
++
++ /* Lanes 4 - 7 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8));
++ dword &= ~(mask << 24);
++ dword &= ~(mask << 16);
++ dword &= ~(mask << 8);
++ dword &= ~mask;
++ dword |= (delay[7] & mask) << 24;
++ dword |= (delay[6] & mask) << 16;
++ dword |= (delay[5] & mask) << 8;
++ dword |= delay[4] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8), dword);
++
++ /* Lane 8 (ECC) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8));
++ dword &= ~mask;
++ dword |= delay[8] & mask;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8), dword);
+ }
+
+ /* DQS Position Training
+ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.3
+ */
+-static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
++static void TrainDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+ u32 Errors;
+@@ -406,7 +497,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->DIMMValidDCT[Channel] == 0) /* mct_BeforeTrainDQSRdWrPos_D */
+ continue;
+
+- index_reg = 0x98 + 0x100 * Channel;
++ index_reg = 0x98;
+
+ dual_rank = 0;
+ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
+@@ -462,7 +553,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ break;
+
+ /* Commit the current Write Data Timing settings to the hardware registers */
+- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Write the DRAM training pattern to the base test address */
+ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
+@@ -479,7 +570,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ current_read_dqs_delay[lane] = test_read_dqs_delay;
+
+ /* Commit the current Read DQS Timing Control settings to the hardware registers */
+- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Initialize test result variable */
+ bytelane_test_results = 0xff;
+@@ -545,7 +636,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ passing_dqs_delay_found[lane] = 1;
+
+ /* Commit the current Read DQS Timing Control settings to the hardware registers */
+- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Exit the DRAM Write Data Timing Loop */
+ write_dqs_delay_stepping_done[lane] = 1;
+@@ -579,7 +670,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ current_write_dqs_delay[lane] = test_write_dqs_delay;
+
+ /* Commit the current Write Data Timing settings to the hardware registers */
+- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Write the DRAM training pattern to the base test address */
+ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
+@@ -674,7 +765,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
+
+ /* Commit the current Read DQS Timing Control settings to the hardware registers */
+- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Save the final Read DQS Timing Control settings for later use */
+ pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
+@@ -717,7 +808,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ current_write_dqs_delay[lane] = (best_pos + (best_count / 2));
+
+ /* Commit the current Write Data Timing settings to the hardware registers */
+- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
+
+ /* Save the final Write Data Timing settings for later use */
+ pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
+@@ -787,6 +878,831 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: Done\n\n");
+ }
+
++/* Calcuate and set MaxRdLatency
++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.5
++ */
++static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t dimm;
++ uint8_t lane;
++ uint32_t dword;
++ uint32_t dword2;
++ uint32_t max_delay;
++ uint8_t mem_clk = 0;
++ uint8_t nb_pstate;
++ uint32_t nb_clk;
++ uint32_t p = 0;
++ uint32_t n = 0;
++ uint32_t t = 0;
++ uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
++ uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
++
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++
++ /* P is specified in PhyCLKs (1/2 MEMCLKs) */
++ for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) {
++ /* 2.10.5.8.5 (2) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
++ if ((!(dword & (0x1 << 21))) && (!(dword & (0x1 << 13))) && (!(dword & (0x1 << 5))))
++ p += 1;
++ else
++ p += 2;
++
++ /* 2.10.5.8.5 (3) */
++ dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210) & 0xf; /* Retrieve RdPtrInit */
++ p += (9 - dword);
++
++ /* 2.10.5.8.5 (4) */
++ p += 5;
++
++ /* 2.10.5.8.5 (5) */
++ dword = Get_NB32_DCT(dev, dct, 0xa8);
++ dword2 = Get_NB32_DCT(dev, dct, 0x90);
++ if ((!(dword & (0x1 << 5))) && (!(dword2 & (0x1 << 16))))
++ p += 2;
++
++ /* 2.10.5.8.5 (6) */
++ dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f; /* Retrieve Tcl */
++ p += (2 * (dword - 1));
++
++ /* 2.10.5.8.5 (7) */
++ max_delay = 0;
++ for (dimm = 0; dimm < 4; dimm++) {
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, dimm * 2))
++ continue;
++
++ read_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
++ read_read_dqs_timing_control_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++)
++ if ((current_phy_phase_delay[lane] + current_read_dqs_delay[lane]) > max_delay)
++ max_delay = (current_phy_phase_delay[lane] + current_read_dqs_delay[lane]);
++ }
++ p += (max_delay >> 5);
++
++ /* 2.10.5.8.5 (8) */
++ p += 5;
++
++ /* 2.10.5.8.5 (9) */
++ t += 800;
++
++ /* 2.10.5.8.5 (10) */
++ mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
++ dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4))); /* Retrieve NbDid, NbFid */
++ nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1);
++ n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
++
++ /* 2.10.5.8.5 (11) */
++ n -= 1;
++
++ /* 2.10.5.8.5 (12) */
++ dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210);
++ dword &= ~(0x3ff << 22);
++ dword |= (((n - 1) & 0x3ff) << 22);
++ Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
++
++ /* Save result for later use */
++ pDCTstat->CH_MaxRdLat[dct] = n;
++ }
++}
++
++static void start_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
++{
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ /* 2.10.5.7.1.1
++ * It appears that the DCT only supports 8-beat burst length mode,
++ * so do nothing here...
++ */
++
++ /* Wait for CmdSendInProg == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ } while (dword & (0x1 << 12));
++
++ /* Set CmdTestEnable = 1 */
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword |= (0x1 << 2);
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.1 Send Activate Command (Target A) */
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */
++ dword |= ((0x1 << Receiver) << 22);
++ dword &= ~(0x7 << 19); /* CmdBank = 0 */
++ dword &= ~(0x3ffff); /* CmdAddress = 0 */
++ dword |= (0x1 << 31); /* SendActCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x28c, dword);
++
++ /* Wait for SendActCmd == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ } while (dword & (0x1 << 31));
++
++ /* Wait 75 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
++
++ /* 2.10.5.8.6.1.1 Send Activate Command (Target B) */
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */
++ dword |= ((0x1 << Receiver) << 22);
++ dword &= ~(0x7 << 19); /* CmdBank = 1 */
++ dword |= (0x1 << 19);
++ dword &= ~(0x3ffff); /* CmdAddress = 0 */
++ dword |= (0x1 << 31); /* SendActCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x28c, dword);
++
++ /* Wait for SendActCmd == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ } while (dword & (0x1 << 31));
++
++ /* Wait 75 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
++}
++
++static void stop_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
++{
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ /* 2.10.5.8.6.1.1 Send Precharge Command */
++ /* Wait 25 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
++
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */
++ dword |= ((0x1 << Receiver) << 22);
++ dword &= ~(0x7 << 19); /* CmdBank = 0 */
++ dword &= ~(0x3ffff); /* CmdAddress = 0x400 */
++ dword |= 0x400;
++ dword |= (0x1 << 30); /* SendPchgCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x28c, dword);
++
++ /* Wait for SendPchgCmd == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ } while (dword & (0x1 << 30));
++
++ /* Wait 25 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
++
++ /* Set CmdTestEnable = 0 */
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword &= ~(0x1 << 2);
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++}
++
++static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
++{
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
++
++ /* 2.10.5.8.6.1.2 */
++ /* Configure DQMask */
++ if (lane < 4) {
++ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ } else if (lane < 8) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ } else {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ }
++
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0 */
++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
++
++ dword = Get_NB32_DCT(dev, dct, 0x270);
++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */
++// dword |= (0x55555);
++ dword |= (0x44443); /* Use AGESA seed */
++ Set_NB32_DCT(dev, dct, 0x270, dword);
++
++ /* 2.10.5.8.4 */
++ dword = Get_NB32_DCT(dev, dct, 0x260);
++ dword &= ~(0x1fffff); /* CmdCount = 256 */
++ dword |= 256;
++ Set_NB32_DCT(dev, dct, 0x260, dword);
++
++ /* Configure Target A */
++ dword = Get_NB32_DCT(dev, dct, 0x254);
++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */
++ dword |= (Receiver & 0x7) << 24;
++ dword &= ~(0x7 << 21); /* TgtBank = 0 */
++ dword &= ~(0x3ff); /* TgtAddress = 0 */
++ Set_NB32_DCT(dev, dct, 0x254, dword);
++
++ /* Configure Target B */
++ dword = Get_NB32_DCT(dev, dct, 0x258);
++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */
++ dword |= (Receiver & 0x7) << 24;
++ dword &= ~(0x7 << 21); /* TgtBank = 1 */
++ dword |= (0x1 << 21);
++ dword &= ~(0x3ff); /* TgtAddress = 0 */
++ Set_NB32_DCT(dev, dct, 0x258, dword);
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword |= (0x1 << 3); /* ResetAllErr = 1 */
++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */
++ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */
++ dword |= (0x1 << 8);
++ dword &= ~(0x7 << 5); /* CmdType = 0 (Read) */
++ dword |= (0x1 << 11); /* SendCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword &= ~(0x1 << 11); /* SendCmd = 0 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
++}
++
++static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
++{
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
++
++ /* 2.10.5.8.6.1.2 */
++ /* Configure DQMask */
++ if (lane < 4) {
++ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ } else if (lane < 8) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ } else {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ }
++
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0 */
++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
++
++ dword = Get_NB32_DCT(dev, dct, 0x270);
++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */
++// dword |= (0x55555);
++ dword |= (0x44443); /* Use AGESA seed */
++ Set_NB32_DCT(dev, dct, 0x270, dword);
++
++ /* 2.10.5.8.4 */
++ dword = Get_NB32_DCT(dev, dct, 0x260);
++ dword &= ~(0x1fffff); /* CmdCount = 256 */
++ dword |= 256;
++ Set_NB32_DCT(dev, dct, 0x260, dword);
++
++ /* Configure Target A */
++ dword = Get_NB32_DCT(dev, dct, 0x254);
++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */
++ dword |= (Receiver & 0x7) << 24;
++ dword &= ~(0x7 << 21); /* TgtBank = 0 */
++ dword &= ~(0x3ff); /* TgtAddress = 0 */
++ Set_NB32_DCT(dev, dct, 0x254, dword);
++
++ /* Configure Target B */
++ dword = Get_NB32_DCT(dev, dct, 0x258);
++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */
++ dword |= (Receiver & 0x7) << 24;
++ dword &= ~(0x7 << 21); /* TgtBank = 1 */
++ dword |= (0x1 << 21);
++ dword &= ~(0x3ff); /* TgtAddress = 0 */
++ Set_NB32_DCT(dev, dct, 0x258, dword);
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword |= (0x1 << 3); /* ResetAllErr = 1 */
++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */
++ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */
++ dword |= (0x1 << 8);
++ dword &= ~(0x7 << 5); /* CmdType = 1 (Write) */
++ dword |= (0x1 << 5);
++ dword |= (0x1 << 11); /* SendCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword &= ~(0x1 << 11); /* SendCmd = 0 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
++}
++
++/* DQS Position Training
++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.4
++ */
++static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t receiver_start, uint8_t receiver_end, uint8_t lane_start, uint8_t lane_end)
++{
++ uint8_t dimm;
++ uint8_t lane;
++ uint32_t dword;
++ uint32_t Errors;
++ uint8_t Receiver;
++ uint8_t dual_rank;
++ uint8_t write_iter;
++ uint8_t read_iter;
++ uint16_t initial_write_dqs_delay[MAX_BYTE_LANES];
++ uint16_t initial_read_dqs_delay[MAX_BYTE_LANES];
++ uint16_t initial_write_data_timing[MAX_BYTE_LANES];
++ uint16_t current_write_data_delay[MAX_BYTE_LANES];
++ uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
++ uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
++ uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
++ uint8_t dqs_results_array[2][(lane_end - lane_start)][32][32]; /* [rank][lane][write step][read step] */
++
++ uint8_t last_pos = 0;
++ uint8_t cur_count = 0;
++ uint8_t best_pos = 0;
++ uint8_t best_count = 0;
++
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ /* Calculate and program MaxRdLatency */
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
++
++ Errors = 0;
++ dual_rank = 0;
++ Receiver = mct_InitReceiver_D(pDCTstat, dct);
++ if (receiver_start > Receiver)
++ Receiver = receiver_start;
++
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each DIMM.
++ */
++ for (; Receiver < receiver_end; Receiver += 2) {
++ dimm = (Receiver >> 1);
++ if ((Receiver & 0x1) == 0) {
++ /* Even rank of DIMM */
++ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver+1))
++ dual_rank = 1;
++ else
++ dual_rank = 0;
++ }
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
++ continue;
++ }
++
++ /* Initialize variables */
++ for (lane = lane_start; lane < lane_end; lane++) {
++ passing_dqs_delay_found[lane] = 0;
++ }
++ memset(dqs_results_array, 0, sizeof(dqs_results_array));
++
++ /* Read initial read / write DQS delays */
++ read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg);
++ read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
++
++ /* Read current settings of other (previously trained) lanes */
++ read_dqs_write_data_timing_registers(initial_write_data_timing, dev, dct, dimm, index_reg);
++ memcpy(current_write_data_delay, initial_write_data_timing, sizeof(current_write_data_delay));
++
++ for (lane = lane_start; lane < lane_end; lane++) {
++ /* 2.10.5.8.4 (2)
++ * For each Write Data Delay value from Write DQS Delay to Write DQS Delay + 1 UI
++ */
++ for (current_write_data_delay[lane] = initial_write_dqs_delay[lane]; current_write_data_delay[lane] < (initial_write_dqs_delay[lane] + 0x20); current_write_data_delay[lane]++) {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 16 current_write_data_delay[lane] ", current_write_data_delay[lane], 6);
++
++ /* 2.10.5.8.4 (2 A)
++ * Commit the current Write Data Timing settings to the hardware registers
++ */
++ write_dqs_write_data_timing_registers(current_write_data_delay, dev, dct, dimm, index_reg);
++
++ /* 2.10.5.8.4 (2 B)
++ * Write the DRAM training pattern to the test address
++ */
++ write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
++
++ /* Read current settings of other (previously trained) lanes */
++ read_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
++
++ /* 2.10.5.8.4 (2 C)
++ * For each Read DQS Delay value from 0 to 1 UI
++ */
++ for (current_read_dqs_delay[lane] = 0; current_read_dqs_delay[lane] < 0x40; current_read_dqs_delay[lane] += 2) {
++ print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 current_read_dqs_delay[lane] ", current_read_dqs_delay[lane], 6);
++
++ /* 2.10.5.8.4 (2 A i)
++ * Commit the current Read DQS Timing Control settings to the hardware registers
++ */
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
++
++ /* 2.10.5.8.4 (2 A ii)
++ * Read the DRAM training pattern from the test address
++ */
++ read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
++
++ /* 2.10.5.8.4 (2 A iii)
++ * Record pass / fail status
++ */
++ dword = Get_NB32_DCT(dev, dct, 0x268) & 0x3ffff;
++ if (dword & (0x3 << (lane * 2)))
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 0; /* Fail */
++ else
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 1; /* Pass */
++ }
++ }
++
++ if (dual_rank && (Receiver & 0x1)) {
++ /* Overlay the previous rank test results with the current rank */
++ for (write_iter = 0; write_iter < 32; write_iter++) {
++ for (read_iter = 0; read_iter < 32; read_iter++) {
++ if ((dqs_results_array[0][lane - lane_start][write_iter][read_iter])
++ && (dqs_results_array[1][lane - lane_start][write_iter][read_iter]))
++ dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 1;
++ else
++ dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 0;
++ }
++ }
++ }
++
++ /* Determine location and length of longest consecutive string of read passing values
++ * Output is stored in best_pos and best_count
++ */
++ last_pos = 0;
++ cur_count = 0;
++ best_pos = 0;
++ best_count = 0;
++ for (write_iter = 0; write_iter < 32; write_iter++) {
++ for (read_iter = 0; read_iter < 32; read_iter++) {
++ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (read_iter < 31)) {
++ /* Pass */
++ cur_count++;
++ } else {
++ /* Failure or end of loop */
++ if (cur_count > best_count) {
++ best_count = cur_count;
++ best_pos = last_pos;
++ }
++ cur_count = 0;
++ last_pos = read_iter;
++ }
++ }
++ last_pos = 0;
++ }
++
++ if (best_count > 2) {
++ /* Restore current settings of other (previously trained) lanes to the active array */
++ memcpy(current_read_dqs_delay, initial_read_dqs_delay, sizeof(current_read_dqs_delay));
++
++ /* Program the Read DQS Timing Control register with the center of the passing window */
++ current_read_dqs_delay[lane] = ((best_pos << 1) + ((best_count << 1) / 2));
++ passing_dqs_delay_found[lane] = 1;
++
++ /* Commit the current Read DQS Timing Control settings to the hardware registers */
++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
++
++ /* Save the final Read DQS Timing Control settings for later use */
++ pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
++
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest read passing region ", best_count, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest read passing region start ", best_pos, 4);
++ } else {
++ /* Reprogram the Read DQS Timing Control register with the original settings */
++ write_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
++ }
++
++ /* Determine location and length of longest consecutive string of write passing values
++ * Output is stored in best_pos and best_count
++ */
++ last_pos = 0;
++ cur_count = 0;
++ best_pos = 0;
++ best_count = 0;
++ for (read_iter = 0; read_iter < 32; read_iter++) {
++ for (write_iter = 0; write_iter < 32; write_iter++) {
++ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (write_iter < 31)) {
++ /* Pass */
++ cur_count++;
++ } else {
++ /* Failure or end of loop */
++ if (cur_count > best_count) {
++ best_count = cur_count;
++ best_pos = last_pos;
++ }
++ cur_count = 0;
++ last_pos = write_iter;
++ }
++ }
++ last_pos = 0;
++ }
++
++ if (best_count > 2) {
++ /* Restore current settings of other (previously trained) lanes to the active array */
++ memcpy(current_write_dqs_delay, initial_write_data_timing, sizeof(current_write_data_delay));
++
++ /* Program the Write DQS Timing Control register with the optimal region within the passing window */
++ if (pDCTstat->Status & (1 << SB_LoadReduced))
++ current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 3));
++ else
++ current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 2));
++ passing_dqs_delay_found[lane] = 1;
++
++ /* Commit the current Write DQS Timing Control settings to the hardware registers */
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg);
++
++ /* Save the final Write Data Timing settings for later use */
++ pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
++
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest write passing region ", best_count, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region start ", best_pos, 4);
++ } else {
++ /* Reprogram the Write DQS Timing Control register with the original settings */
++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg);
++ }
++ }
++
++#ifdef PRINT_PASS_FAIL_BITMAPS
++ for (lane = lane_start; lane < lane_end; lane++) {
++ for (read_iter = 0; read_iter < 32; read_iter++) {
++ for (write_iter = 0; write_iter < 32; write_iter++) {
++ if (dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
++ printk(BIOS_DEBUG, "\n\n");
++ }
++#endif
++
++ /* Flag failure(s) if present */
++ for (lane = lane_start; lane < lane_end; lane++) {
++ if (!passing_dqs_delay_found[lane]) {
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 121 Unable to find passing region for lane ", lane, 2);
++
++ /* Flag absence of passing window */
++ Errors |= 1 << SB_NODQSPOS;
++ }
++ }
++
++ pDCTstat->TrainErrors |= Errors;
++ pDCTstat->ErrStatus |= Errors;
++
++#if DQS_TRAIN_DEBUG > 0
++ {
++ u8 val;
++ u8 i;
++ u8 ChannelDTD, ReceiverDTD, Dir;
++ u8 *p;
++
++ for (Dir = 0; Dir < 2; Dir++) {
++ if (Dir == 1) {
++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n");
++ } else {
++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
++ }
++ for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
++ for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
++ printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
++ p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
++ for (i=0;i<8; i++) {
++ val = p[i];
++ printk(BIOS_DEBUG, " %02x", val);
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
++ }
++ }
++
++ }
++#endif
++ }
++
++ /* Return 1 on success, 0 on failure */
++ return !Errors;
++}
++
++/* DQS Receiver Enable Cycle Training
++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.3
++ */
++static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ u32 Errors;
++ u8 Receiver;
++ u8 _DisableDramECC = 0;
++ u8 _Wrap32Dis = 0, _SSE2 = 0;
++
++ u32 addr;
++ u32 cr4;
++ u32 lo, hi;
++
++ uint8_t dct;
++ uint8_t prev;
++ uint8_t dimm;
++ uint8_t lane;
++ uint32_t dword;
++ uint32_t rx_en_offset;
++ uint16_t initial_phy_phase_delay[MAX_BYTE_LANES];
++ uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
++ uint8_t dqs_results_array[1024];
++
++ uint16_t ren_step = 0x40;
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ print_debug_dqs("\nTrainDQSReceiverEnCyc: Node_ID ", pDCTstat->Node_ID, 0);
++ cr4 = read_cr4();
++ if (cr4 & (1<<9)) {
++ _SSE2 = 1;
++ }
++ cr4 |= (1<<9); /* OSFXSR enable SSE2 */
++ write_cr4(cr4);
++
++ addr = HWCR;
++ _RDMSR(addr, &lo, &hi);
++ if (lo & (1<<17)) {
++ _Wrap32Dis = 1;
++ }
++ lo |= (1<<17); /* HWCR.wrap32dis */
++ _WRMSR(addr, lo, hi); /* allow 64-bit memory references in real mode */
++
++ /* Disable ECC correction of reads on the dram bus. */
++ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
++
++ Errors = 0;
++
++ for (dct = 0; dct < 2; dct++) {
++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
++ dword &= ~(0x3 << 13);
++ dword |= (0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
++ }
++
++ for (dct = 0; dct < 2; dct++) {
++ /* 2.10.5.6 */
++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1);
++
++ /* 2.10.5.8.3 */
++ Receiver = mct_InitReceiver_D(pDCTstat, dct);
++
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each DIMM.
++ */
++ for (; Receiver < 8; Receiver += 2) {
++ dimm = (Receiver >> 1);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
++ continue;
++ }
++
++ /* 2.10.5.8.3 (2) */
++ read_dqs_receiver_enable_control_registers(initial_phy_phase_delay, dev, dct, dimm, index_reg);
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ /* Initialize variables */
++ memset(dqs_results_array, 0, sizeof(dqs_results_array));
++
++ /* 2.10.5.8.3 (1) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8));
++ dword |= (0x1 << 8); /* BlockRxDqsLock = 1 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword);
++
++ /* 2.10.5.8.3 (3) */
++ rx_en_offset = (initial_phy_phase_delay[lane] + 0x10) % 0x40;
++
++ /* 2.10.5.8.3 (4) */
++ for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
++ /* 2.10.5.8.3 (4 A) */
++ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
++
++ /* Calculate and program MaxRdLatency */
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
++
++ /* 2.10.5.8.3 (4 B) */
++ dqs_results_array[current_phy_phase_delay[lane]] = TrainDQSRdWrPos_D_Fam15(pMCTstat, pDCTstat, dct, Receiver, Receiver + 2, lane, lane + 1);
++ }
++
++#ifdef PRINT_PASS_FAIL_BITMAPS
++ uint16_t iter;
++ for (iter = 0; iter < 0x3ff; iter++) {
++ if (dqs_results_array[iter])
++ printk(BIOS_DEBUG, "+");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
++ printk(BIOS_DEBUG, "\n");
++#endif
++
++ /* 2.10.5.8.3 (5) */
++ prev = 0;
++ for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
++ if ((dqs_results_array[current_phy_phase_delay[lane]] == 0) && (prev == 1)) {
++ /* Restore last known good delay */
++ current_phy_phase_delay[lane] -= ren_step;
++
++ /* 2.10.5.8.3 (5 A B) */
++ current_phy_phase_delay[lane] -= 0x10;
++
++ /* Update hardware registers with final values */
++ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
++ break;
++ }
++ prev = dqs_results_array[current_phy_phase_delay[lane]];
++ }
++
++ /* 2.10.5.8.3 (6) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8));
++ dword &= ~(0x1 << 8); /* BlockRxDqsLock = 0 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword);
++ }
++
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 DQS receiver enable timing: ");
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ printk(BIOS_DEBUG, " %03x", current_phy_phase_delay[lane]);
++ }
++ printk(BIOS_DEBUG, "\n");
++#endif
++ }
++ }
++
++ pDCTstat->TrainErrors |= Errors;
++ pDCTstat->ErrStatus |= Errors;
++
++#if DQS_TRAIN_DEBUG > 0
++ {
++ u8 val;
++ u8 i;
++ u8 ChannelDTD, ReceiverDTD, Dir;
++ u8 *p;
++
++ for (Dir = 0; Dir < 2; Dir++) {
++ if (Dir == 1) {
++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n");
++ } else {
++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
++ }
++ for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
++ for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
++ printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
++ p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
++ for (i=0;i<8; i++) {
++ val = p[i];
++ printk(BIOS_DEBUG, " %02x", val);
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
++ }
++ }
++
++ }
++#endif
++ if (_DisableDramECC) {
++ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
++ }
++ if (!_Wrap32Dis) {
++ addr = HWCR;
++ _RDMSR(addr, &lo, &hi);
++ lo &= ~(1<<17); /* restore HWCR.wrap32dis */
++ _WRMSR(addr, lo, hi);
++ }
++ if (!_SSE2){
++ cr4 = read_cr4();
++ cr4 &= ~(1<<9); /* restore cr4.OSFXSR */
++ write_cr4(cr4);
++ }
++
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Status %x\n", pDCTstat->Status);
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: TrainErrors %x\n", pDCTstat->TrainErrors);
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrStatus %x\n", pDCTstat->ErrStatus);
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrCode %x\n", pDCTstat->ErrCode);
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Done\n\n");
++}
++
+ static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 *buffer)
+ {
+@@ -869,18 +1785,17 @@ static u8 ChipSelPresent_D(struct MCTStatStruc *pMCTstat,
+ u32 val;
+ u32 reg;
+ u32 dev = pDCTstat->dev_dct;
+- u32 reg_off;
++ uint8_t dct = 0;
+ u8 ret = 0;
+
+- if (!pDCTstat->GangedMode) {
+- reg_off = 0x100 * Channel;
+- } else {
+- reg_off = 0;
+- }
++ if (!pDCTstat->GangedMode)
++ dct = Channel;
++ else
++ dct = 0;
+
+ if (ChipSel < MAX_CS_SUPPORTED){
+- reg = 0x40 + (ChipSel << 2) + reg_off;
+- val = Get_NB32(dev, reg);
++ reg = 0x40 + (ChipSel << 2);
++ val = Get_NB32_DCT(dev, dct, reg);
+ if (val & ( 1 << 0))
+ ret = 1;
+ }
+@@ -1085,12 +2000,12 @@ u32 SetUpperFSbase(u32 addr_hi)
+ return addr_hi << 8;
+ }
+
+-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index)
++void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index)
+ {
+ u32 val;
+
+- val = Get_NB32_index_wait(dev, index_reg, index);
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+ }
+
+ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+@@ -1103,9 +2018,13 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->DCTSysLimit) {
+- TrainDQSRdWrPos_D(pMCTstat, pDCTstat);
+- for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+- SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
++ if (is_fam15h()) {
++ TrainDQSReceiverEnCyc_D_Fam15(pMCTstat, pDCTstat);
++ } else {
++ TrainDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat);
++ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
++ SetEccDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat, ChipSel);
++ }
+ }
+ }
+ }
+@@ -1126,19 +2045,18 @@ u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+
+ dev = pDCTstat->dev_dct;
+ reg = 0x90;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 0, reg);
+ if (val & (1<<DimmEcEn)) {
+ _DisableDramECC |= 0x01;
+ val &= ~(1<<DimmEcEn);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 0, reg, val);
+ }
+ if (!pDCTstat->GangedMode) {
+- reg = 0x190;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 1, reg);
+ if (val & (1<<DimmEcEn)) {
+ _DisableDramECC |= 0x02;
+ val &= ~(1<<DimmEcEn);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 1, reg, val);
+ }
+ }
+ return _DisableDramECC;
+@@ -1157,15 +2075,14 @@ void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+
+ if ((_DisableDramECC & 0x01) == 0x01) {
+ reg = 0x90;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 0, reg);
+ val |= (1<<DimmEcEn);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 0, reg, val);
+ }
+ if ((_DisableDramECC & 0x02) == 0x02) {
+- reg = 0x190;
+- val = Get_NB32(dev, reg);
++ val = Get_NB32_DCT(dev, 1, reg);
+ val |= (1<<DimmEcEn);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, 1, reg, val);
+ }
+ }
+
+@@ -1177,7 +2094,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ {
+ u8 ByteLane;
+ u32 val;
+- u32 index_reg = 0x98 + 0x100 * pDCTstat->Channel;
++ u32 index_reg = 0x98;
+ u8 shift;
+ u32 dqs_delay = (u32)pDCTstat->DQSDelay;
+ u32 dev = pDCTstat->dev_dct;
+@@ -1205,7 +2122,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+
+ index += (ChipSel>>1) << 8;
+
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index);
+ if (ByteLane < 8) {
+ if (pDCTstat->Direction == DQS_WRITEDIR) {
+ dqs_delay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][ChipSel>>1][ByteLane];
+@@ -1215,7 +2132,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ }
+ val &= ~(0x7f << shift);
+ val |= (dqs_delay << shift);
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index, val);
+ }
+ }
+
+@@ -1241,7 +2158,7 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+ u8 Channel, u8 receiver, u8 *valid)
+ {
+ u32 val;
+- u32 reg_off = 0;
++ uint8_t dct = 0;
+ u32 reg;
+ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+@@ -1250,12 +2167,12 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+
+
+ if (!pDCTstat->GangedMode) {
+- reg_off = 0x100 * Channel;
++ dct = Channel;
+ }
+
+ /* get the local base addr of the chipselect */
+- reg = 0x40 + (receiver << 2) + reg_off;
+- val = Get_NB32(dev, reg);
++ reg = 0x40 + (receiver << 2);
++ val = Get_NB32_DCT(dev, dct, reg);
+
+ val &= ~0xe007c01f;
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index 0c52791..968f5e5 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -91,19 +92,21 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+
+ /* Construct these booleans, based on setup options, for easy handling
+ later in this procedure */
+- OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
++ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
+
+- OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
++ OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
+
+- OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
++ OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
+
+- OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
+- nvbits = mctGet_NVbits(NV_DCBKScrub);
+- /* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */
+- OF_ScrubCTL |= (u32) nvbits << 16;
++ if (!is_fam15h()) {
++ OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
++ nvbits = mctGet_NVbits(NV_DCBKScrub);
++ /* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */
++ OF_ScrubCTL |= (u32) nvbits << 16;
+
+- nvbits = mctGet_NVbits(NV_L2BKScrub);
+- OF_ScrubCTL |= (u32) nvbits << 8;
++ nvbits = mctGet_NVbits(NV_L2BKScrub);
++ OF_ScrubCTL |= (u32) nvbits << 8;
++ }
+
+ nvbits = mctGet_NVbits(NV_DramBKScrub);
+ OF_ScrubCTL |= nvbits;
+@@ -131,7 +134,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
+ }
+ AllECC = 0;
+- LDramECC =0;
++ LDramECC = 0;
+ }
+ } else {
+ AllECC = 0;
+@@ -140,7 +143,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ if (OB_NBECC) {
+ mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
+ dev = pDCTstat->dev_nbmisc;
+- reg =0x44; /* MCA NB Configuration */
++ reg = 0x44; /* MCA NB Configuration */
+ val = Get_NB32(dev, reg);
+ val |= 1 << 22; /* EccEn */
+ Set_NB32(dev, reg, val);
+@@ -177,6 +180,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ /*WE/RE is checked because memory config may have been */
+ if((val & 3)==3) { /* Node has dram populated */
+ if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
++ if (is_fam15h()) {
++ /* Erratum 505 */
++ fam15h_switch_dct(pDCTstat->dev_map, 0);
++ }
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+ if(OB_ECCRedir) {
+@@ -187,16 +194,18 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+ Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
+
+- /* Divisor should not be set deeper than
+- * divide by 16 when Dcache scrubber or
+- * L2 scrubber is enabled.
+- */
+- if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
+- val = Get_NB32(dev, 0x84);
+- if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
+- val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
+- val |= 0x80000000; /* set it to divide-by-16 */
+- Set_NB32(dev, 0x84, val);
++ if (!is_fam15h()) {
++ /* Divisor should not be set deeper than
++ * divide by 16 when Dcache scrubber or
++ * L2 scrubber is enabled.
++ */
++ if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
++ val = Get_NB32(dev, 0x84);
++ if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
++ val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
++ val |= 0x80000000; /* set it to divide-by-16 */
++ Set_NB32(dev, 0x84, val);
++ }
+ }
+ }
+ } /* this node has ECC enabled dram */
+@@ -267,8 +276,8 @@ static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
+ }
+ for(i=0; i<ch_end; i++) {
+ if(pDCTstat->DIMMValidDCT[i] > 0){
+- reg = 0x90 + i * 0x100; /* Dram Config Low */
+- val = Get_NB32(dev, reg);
++ reg = 0x90; /* Dram Config Low */
++ val = Get_NB32_DCT(dev, i, reg);
+ if(val & (1<<DimmEcEn)) {
+ /* set local flag 'dram ecc capable' */
+ isDimmECCEn = 1;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
+index 0112732..a6b9dcb 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -25,8 +26,8 @@ void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+
+ /*flag for selecting HW/SW DRAM Init HW DRAM Init */
+- reg = 0x90 + 0x100 * dct; /*DRAM Configuration Low */
+- val = Get_NB32(dev, reg);
++ reg = 0x90; /*DRAM Configuration Low */
++ val = Get_NB32_DCT(dev, dct, reg);
+ val |= (1<<InitDram);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, dct, reg, val);
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+index 60bc01d..5e81808 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+@@ -18,10 +18,12 @@
+ * Foundation, Inc.
+ */
+
+-static void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat);
+-static void AgesaHwWlPhase1(sMCTStruct *pMCTData,
+- sDCTStruct *pDCTData, u8 dimm, u8 pass);
++static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
++static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
++static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+ static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+@@ -56,7 +58,7 @@ static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pD
+ Addl_Index = 0x32;
+ Addl_Index += DimmNum * 3;
+
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index);
++ val = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, Channel, 0x98, Addl_Index);
+ if (OddByte)
+ val >>= 16;
+ /* Save WrDqs to stack for later usage */
+@@ -74,13 +76,13 @@ static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStr
+ {
+ u32 val;
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x8C);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C);
+ val &= ~(1 << DisAutoRefresh);
+- Set_NB32(pDCTstat->dev_dct, 0x8C, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val);
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C);
+ val &= ~(1 << DisAutoRefresh);
+- Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val);
+ }
+
+ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
+@@ -88,13 +90,13 @@ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x8C);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C);
+ val |= 1 << DisAutoRefresh;
+- Set_NB32(pDCTstat->dev_dct, 0x8C, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val);
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C);
+ val |= 1 << DisAutoRefresh;
+- Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val);
+ }
+
+
+@@ -118,8 +120,11 @@ static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ DIMMValid = pDCTstat->DIMMValid;
+ PrepareC_DCT(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+- if (DIMMValid & (1 << (dimm << 1)))
+- AgesaHwWlPhase1(pDCTstat->C_MCTPtr, DCTPtr, dimm, FirstPass);
++ if (DIMMValid & (1 << (dimm << 1))) {
++ AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ }
+ }
+ }
+ }
+@@ -146,27 +151,40 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
+ pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL;
+ SPD2ndTiming(pMCTstat, pDCTstat, dct);
+- ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+- PlatformSpec_D(pMCTstat, pDCTstat, dct);
+- fenceDynTraining_D(pMCTstat, pDCTstat, dct);
++ if (!is_fam15h()) {
++ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
++ PlatformSpec_D(pMCTstat, pDCTstat, dct);
++ fenceDynTraining_D(pMCTstat, pDCTstat, dct);
++ }
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ StartupDCT_D(pMCTstat, pDCTstat, dct);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+- if (DIMMValid & (1 << (dimm << 1)))
+- AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
++ if (DIMMValid & (1 << (dimm << 1))) {
++ AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ }
+ }
+ }
+ }
+
++static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
++{
++ uint16_t fam15h_next_highest_freq_tab[] = {0, 0, 0, 0, 0x6, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12, 0, 0, 0, 0x16, 0, 0, 0, 0x16};
++ return fam15h_next_highest_freq_tab[memclk_freq];
++}
++
+ /* Write Levelization Training
+ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1
+ */
+ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat)
++ struct DCTStatStruc *pDCTstat, uint8_t Pass)
+ {
++ uint16_t final_target_freq;
++
+ pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
+ pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
+ pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
+@@ -182,16 +200,39 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0];
+ }
+
+- PhyWLPass1(pMCTstat, pDCTstat, 0);
+- PhyWLPass1(pMCTstat, pDCTstat, 1);
++ if (Pass == FirstPass) {
++ PhyWLPass1(pMCTstat, pDCTstat, 0);
++ PhyWLPass1(pMCTstat, pDCTstat, 1);
++ }
++
++ if (Pass == SecondPass) {
++ if (pDCTstat->TargetFreq > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
++ /* 8.Prepare the memory subsystem for the target MEMCLK frequency.
++ * NOTE: BIOS must program both DCTs to the same frequency.
++ * NOTE: Fam15h steps the frequency, Fam10h slams the frequency.
++ */
++ final_target_freq = pDCTstat->TargetFreq;
++
++ while (pDCTstat->Speed != final_target_freq) {
++ if (is_fam15h())
++ pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed);
++ else
++ pDCTstat->TargetFreq = final_target_freq;
++ SetTargetFreq(pMCTstat, pDCTstat);
++ PhyWLPass2(pMCTstat, pDCTstat, 0);
++ PhyWLPass2(pMCTstat, pDCTstat, 1);
++ }
++
++ pDCTstat->TargetFreq = final_target_freq;
+
+- if (pDCTstat->TargetFreq > 4) {
+- /* 8.Prepare the memory subsystem for the target MEMCLK frequency.
+- * Note: BIOS must program both DCTs to the same frequency.
+- */
+- SetTargetFreq(pMCTstat, pDCTstat);
+- PhyWLPass2(pMCTstat, pDCTstat, 0);
+- PhyWLPass2(pMCTstat, pDCTstat, 1);
++ uint8_t dct;
++ for (dct = 0; dct < 2; dct++) {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++ memcpy(pDCTData->WLGrossDelayFinalPass, pDCTData->WLGrossDelayPrevPass, sizeof(pDCTData->WLGrossDelayPrevPass));
++ memcpy(pDCTData->WLFineDelayFinalPass, pDCTData->WLFineDelayPrevPass, sizeof(pDCTData->WLFineDelayPrevPass));
++ pDCTData->WLCriticalGrossDelayFinalPass = pDCTData->WLCriticalGrossDelayPrevPass;
++ }
++ }
+ }
+
+ SetEccWrDQS_D(pMCTstat, pDCTstat);
+@@ -200,7 +241,7 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ }
+
+ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA)
++ struct DCTStatStruc *pDCTstatA, uint8_t Pass)
+ {
+ u8 Node;
+
+@@ -211,7 +252,7 @@ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->NodePresent) {
+ mctSMBhub_Init(Node);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+- WriteLevelization_HW(pMCTstat, pDCTstat);
++ WriteLevelization_HW(pMCTstat, pDCTstat, Pass);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index cda9c6b..5ef4a2c 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -34,7 +34,7 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+
+ if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+ misc2 |= 1 << OdtSwizzle;
+- val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x78);
+
+ val &= 7;
+ val = ((~val) & 0xff) + 1;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+index bd8b7fb..5ea7fa6 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -23,7 +24,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ u8 Dimms, DimmNum, MaxDimm, Speed;
+ u32 val;
+ u32 dct = 0;
+- u32 reg_off = 0;
+
+ DimmNum = (MrsChipSel >> 20) & 0xFE;
+
+@@ -41,7 +41,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ dct = 1;
+ DimmNum ++;
+ }
+- reg_off = 0x100 * dct;
+ Dimms = pDCTstat->MAdimms[dct];
+
+ val = 0;
+@@ -95,21 +94,21 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 val)
+ {
+- u32 reg_off = 0;
++ uint8_t dct = 0;
+ u32 dev = pDCTstat->dev_dct;
+
+ if (pDCTstat->CSPresent_DCT[0] > 0) {
+- reg_off = 0;
++ dct = 0;
+ } else if (pDCTstat->CSPresent_DCT[1] > 0 ){
+- reg_off = 0x100;
++ dct = 1;
+ }
+
+- val |= Get_NB32(dev, reg_off + 0x7C) & ~0xFFFFFF;
++ val |= Get_NB32_DCT(dev, dct, 0x7C) & ~0xFFFFFF;
+ val |= 1 << SendControlWord;
+- Set_NB32(dev, reg_off + 0x7C, val);
++ Set_NB32_DCT(dev, dct, 0x7C, val);
+
+ do {
+- val = Get_NB32(dev, reg_off + 0x7C);
++ val = Get_NB32_DCT(dev, dct, 0x7C);
+ } while (val & (1 << SendControlWord));
+ }
+
+@@ -119,7 +118,6 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ u8 MrsChipSel;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val, cw;
+- u32 reg_off = 0x100 * dct;
+
+ mct_Wait(1600);
+
+@@ -127,7 +125,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+- val = Get_NB32(dev, reg_off + 0xA8);
++ val = Get_NB32_DCT(dev, dct, 0xa8);
+ val &= ~(0xF << 8);
+
+ switch (MrsChipSel) {
+@@ -144,7 +142,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ case 7:
+ val |= (3 << 6) << 8;
+ }
+- Set_NB32(dev, reg_off + 0xA8 , val);
++ Set_NB32_DCT(dev, dct, 0xa8, val);
+
+ for (cw=0; cw <=15; cw ++) {
+ mct_Wait(1600);
+@@ -171,10 +169,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
+- val = Get_NB32(dev, 0xA8); /* TODO: dct * 0x100 + 0xA8 */
++ val = Get_NB32_DCT(dev, 0, 0xA8); /* TODO: dct 0 / 1 select */
+ val &= ~(0xFF << 8);
+ val |= (0x3 << (MrsChipSel & 0xFE)) << 8;
+- Set_NB32(dev, 0xA8, val); /* TODO: dct * 0x100 + 0xA8 */
++ Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */
+
+ /* Resend control word 10 */
+ mct_Wait(1600);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index b21b96a..51cbf16 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -18,17 +18,182 @@
+ * Foundation, Inc.
+ */
+
++static uint8_t fam15_dimm_dic(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
++{
++ uint8_t dic;
++
++ /* Calculate DIC based on recommendations in MR1_dct[1:0] */
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* TODO
++ * LRDIMM unimplemented
++ */
++ dic = 0x0;
++ } else {
++ dic = 0x1;
++ }
++
++ return dic;
++}
++
++static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
++{
++ uint8_t term = 0;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
++ uint8_t frequency_index;
++ uint8_t rank_count = pDCTData->DimmRanks[dimm];
++
++ if (is_fam15h())
++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++ else
++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (is_fam15h()) {
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* TODO
++ * RDIMM unimplemented
++ */
++ } else {
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 56 */
++ if (MaxDimmsInstallable == 1) {
++ term = 0x0;
++ } else if (MaxDimmsInstallable == 2) {
++ if ((number_of_dimms == 2) && (frequency_index == 0x12)) {
++ term = 0x1;
++ } else if (number_of_dimms == 1) {
++ term = 0x0;
++ } else {
++ term = 0x2;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ if (number_of_dimms == 1) {
++ if (frequency_index <= 0xa) {
++ term = 0x2;
++ } else {
++ if (rank_count < 3) {
++ term = 0x1;
++ } else {
++ term = 0x2;
++ }
++ }
++ } else if (number_of_dimms == 2) {
++ term = 0x2;
++ }
++ }
++ } else {
++ /* TODO
++ * Other sockets unimplemented
++ */
++ }
++ }
++ }
++
++ return term;
++}
++
++static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
++{
++ uint8_t term = 0;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
++ uint8_t frequency_index;
++
++ if (is_fam15h())
++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++ else
++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (is_fam15h()) {
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* TODO
++ * LRDIMM unimplemented
++ */
++ } else if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* TODO
++ * RDIMM unimplemented
++ */
++ } else {
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 56 */
++ if (MaxDimmsInstallable == 1) {
++ if ((frequency_index == 0x4) || (frequency_index == 0x6))
++ term = 0x2;
++ else if ((frequency_index == 0xa) || (frequency_index == 0xe))
++ term = 0x1;
++ else
++ term = 0x3;
++ }
++ if (MaxDimmsInstallable == 2) {
++ if (number_of_dimms == 1) {
++ if (frequency_index <= 0x6) {
++ term = 0x2;
++ } else if (frequency_index <= 0xe) {
++ term = 0x1;
++ } else {
++ term = 0x3;
++ }
++ } else {
++ if (frequency_index <= 0xa) {
++ term = 0x3;
++ } else if (frequency_index <= 0xe) {
++ term = 0x5;
++ } else {
++ term = 0x4;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ if (number_of_dimms == 1) {
++ term = 0x0;
++ } else if (number_of_dimms == 2) {
++ if (frequency_index <= 0xa) {
++ if (rank == 1) {
++ term = 0x0;
++ } else {
++ term = 0x3;
++ }
++ } else if (frequency_index <= 0xe) {
++ if (rank == 1) {
++ term = 0x0;
++ } else {
++ term = 0x5;
++ }
++ }
++ }
++ }
++ } else {
++ /* TODO
++ * Other sockets unimplemented
++ */
++ }
++ }
++ }
++
++ return term;
++}
++
+ static void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+
+ static void mct_DCTAccessDone(struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
+ do {
+- val = Get_NB32(dev, reg_off + 0x98);
++ val = Get_NB32_DCT(dev, dct, 0x98);
+ } while (!(val & (1 << DctAccessDone)));
+ }
+
+@@ -54,9 +219,15 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting,
+ if (MR_register_setting & (1 << 6)) ret |= 1 << 5;
+ if (MR_register_setting & (1 << 7)) ret |= 1 << 8;
+ if (MR_register_setting & (1 << 8)) ret |= 1 << 7;
+- if (MR_register_setting & (1 << 16)) ret |= 1 << 17;
+- if (MR_register_setting & (1 << 17)) ret |= 1 << 16;
+- MR_register_setting &= ~0x301f8;
++ if (is_fam15h()) {
++ if (MR_register_setting & (1 << 18)) ret |= 1 << 19;
++ if (MR_register_setting & (1 << 19)) ret |= 1 << 18;
++ MR_register_setting &= ~0x000c01f8;
++ } else {
++ if (MR_register_setting & (1 << 16)) ret |= 1 << 17;
++ if (MR_register_setting & (1 << 17)) ret |= 1 << 16;
++ MR_register_setting &= ~0x000301f8;
++ }
+ MR_register_setting |= ret;
+ }
+ }
+@@ -65,47 +236,76 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting,
+
+ static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
+- val = Get_NB32(dev, reg_off + 0x7C);
+- val &= ~0xFFFFFF;
++ val = Get_NB32_DCT(dev, dct, 0x7c);
++ val &= ~0x00ffffff;
+ val |= EMRS;
+ val |= 1 << SendMrsCmd;
+- Set_NB32(dev, reg_off + 0x7C, val);
++ Set_NB32_DCT(dev, dct, 0x7c, val);
+
+ do {
+- val = Get_NB32(dev, reg_off + 0x7C);
++ val = Get_NB32_DCT(dev, dct, 0x7c);
+ } while (val & (1 << SendMrsCmd));
+ }
+
+ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
+- ret = 0x20000;
+- ret |= MrsChipSel;
++ if (is_fam15h()) {
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
+
+- /* program MrsAddress[5:3]=CAS write latency (CWL):
+- * based on F2x[1,0]84[Tcwl] */
+- dword = Get_NB32(dev, reg_off + 0x84);
+- dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword);
++ /* FIXME: These parameters should be configurable
++ * For now, err on the side of caution and enable automatic 2x refresh
++ * when the DDR temperature rises above the internal limits
++ */
++ uint8_t force_2x_self_refresh = 0; /* ASR */
++ uint8_t auto_2x_self_refresh = 1; /* SRT */
+
+- ret |= ((dword >> 20) & 7) << 3;
++ ret = 0x80000;
++ ret |= (MrsChipSel << 21);
+
+- /* program MrsAddress[6]=auto self refresh method (ASR):
+- based on F2x[1,0]84[ASR]
+- program MrsAddress[7]=self refresh temperature range (SRT):
+- based on F2x[1,0]84[ASR and SRT] */
+- ret |= ((dword >> 18) & 3) << 6;
++ /* Set self refresh parameters */
++ ret |= (force_2x_self_refresh << 6);
++ ret |= (auto_2x_self_refresh << 7);
+
+- /* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR)
+- based on F2x[1,0]84[DramTermDyn] */
+- ret |= ((dword >> 10) & 3) << 9;
++ /* Obtain Tcwl, adjust, and set CWL with the adjusted value */
++ dword = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f;
++ ret |= ((dword - 5) << 3);
++
++ /* Obtain and set RttWr */
++ ret |= (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
++ } else {
++ ret = 0x20000;
++ ret |= (MrsChipSel << 20);
++
++ /* program MrsAddress[5:3]=CAS write latency (CWL):
++ * based on F2x[1,0]84[Tcwl] */
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword);
++
++ ret |= ((dword >> 20) & 7) << 3;
++
++ /* program MrsAddress[6]=auto self refresh method (ASR):
++ * based on F2x[1,0]84[ASR]
++ * program MrsAddress[7]=self refresh temperature range (SRT):
++ * based on F2x[1,0]84[ASR and SRT]
++ */
++ ret |= ((dword >> 18) & 3) << 6;
++
++ /* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR)
++ * based on F2x[1,0]84[DramTermDyn]
++ */
++ ret |= ((dword >> 10) & 3) << 9;
++ }
+
+ return ret;
+ }
+@@ -113,20 +313,28 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
+- ret = 0x30000;
+- ret |= MrsChipSel;
++ if (is_fam15h()) {
++ ret = 0xc0000;
++ ret |= (MrsChipSel << 21);
+
+- /* program MrsAddress[1:0]=multi purpose register address location
+- (MPR Location):based on F2x[1,0]84[MprLoc]
+- program MrsAddress[2]=multi purpose register
+- (MPR):based on F2x[1,0]84[MprEn]
+- */
+- dword = Get_NB32(dev, reg_off + 0x84);
+- ret |= (dword >> 24) & 7;
++ /* Program MPR and MPRLoc to 0 */
++ // ret |= 0x0; /* MPR */
++ // ret |= (0x0 << 2); /* MPRLoc */
++ } else {
++ ret = 0x30000;
++ ret |= (MrsChipSel << 20);
++
++ /* program MrsAddress[1:0]=multi purpose register address location
++ * (MPR Location):based on F2x[1,0]84[MprLoc]
++ * program MrsAddress[2]=multi purpose register
++ * (MPR):based on F2x[1,0]84[MprEn]
++ */
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ ret |= (dword >> 24) & 7;
++ }
+
+ return ret;
+ }
+@@ -134,48 +342,93 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
+ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
+- ret = 0x10000;
+- ret |= MrsChipSel;
+-
+- /* program MrsAddress[5,1]=output driver impedance control (DIC):
+- * based on F2x[1,0]84[DrvImpCtrl] */
+- dword = Get_NB32(dev, reg_off + 0x84);
+- if (dword & (1 << 3))
+- ret |= 1 << 5;
+- if (dword & (1 << 2))
+- ret |= 1 << 1;
+-
+- /* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT):
+- based on F2x[1,0]84[DramTerm] */
+- if (!(pDCTstat->Status & (1 << SB_Registered))) {
+- if (dword & (1 << 9))
+- ret |= 1 << 9;
+- if (dword & (1 << 8))
+- ret |= 1 << 6;
+- if (dword & (1 << 7))
+- ret |= 1 << 2;
++ if (is_fam15h()) {
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ /* Set defaults */
++ uint8_t qoff = 0; /* Enable output buffers */
++ uint8_t wrlvl = 0; /* Disable write levelling */
++ uint8_t tqds = 0;
++ uint8_t rttnom = 0;
++ uint8_t dic = 0;
++ uint8_t additive_latency = 0;
++ uint8_t dll_enable = 0;
++
++ ret = 0x40000;
++ ret |= (MrsChipSel << 21);
++
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
++
++ /* Determine if TQDS should be set */
++ if ((pDCTstat->Dimmx8Present & (1 << dimm))
++ && (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0)
++ && (pDCTstat->Status & (1 << SB_LoadReduced)))
++ tqds = 1;
++
++ /* Obtain RttNom */
++ rttnom = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
++
++ /* Obtain DIC */
++ dic = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
++
++ /* Load data into MRS word */
++ ret |= (qoff & 0x1) << 12;
++ ret |= (tqds & 0x1) << 11;
++ ret |= ((rttnom & 0x4) >> 2) << 9;
++ ret |= ((rttnom & 0x2) >> 1) << 6;
++ ret |= ((rttnom & 0x1) >> 0) << 2;
++ ret |= (wrlvl & 0x1) << 7;
++ ret |= ((dic & 0x2) >> 1) << 5;
++ ret |= ((dic & 0x1) >> 0) << 1;
++ ret |= (additive_latency & 0x3) << 3;
++ ret |= (dll_enable & 0x1);
+ } else {
+- ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel);
+- }
++ ret = 0x10000;
++ ret |= (MrsChipSel << 20);
++
++ /* program MrsAddress[5,1]=output driver impedance control (DIC):
++ * based on F2x[1,0]84[DrvImpCtrl]
++ */
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ if (dword & (1 << 3))
++ ret |= 1 << 5;
++ if (dword & (1 << 2))
++ ret |= 1 << 1;
++
++ /* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT):
++ * based on F2x[1,0]84[DramTerm]
++ */
++ if (!(pDCTstat->Status & (1 << SB_Registered))) {
++ if (dword & (1 << 9))
++ ret |= 1 << 9;
++ if (dword & (1 << 8))
++ ret |= 1 << 6;
++ if (dword & (1 << 7))
++ ret |= 1 << 2;
++ } else {
++ ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel);
++ }
+
+- /* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */
+- if (Get_NB32(dev, reg_off + 0x94) & (1 << RDqsEn)) {
+- u8 bit;
+- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+- bit = (ret >> 21) << 1;
+- if ((dct & 1) != 0)
+- bit ++;
+- if (pDCTstat->Dimmx8Present & (1 << bit))
+- ret |= 1 << 11;
+- }
++ /* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */
++ if (Get_NB32_DCT(dev, dct, 0x94) & (1 << RDqsEn)) {
++ u8 bit;
++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
++ bit = (ret >> 21) << 1;
++ if ((dct & 1) != 0)
++ bit ++;
++ if (pDCTstat->Dimmx8Present & (1 << bit))
++ ret |= 1 << 11;
++ }
+
+- /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
+- if (dword & (1 << 13))
+- ret |= 1 << 12;
++ /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
++ if (dword & (1 << 13))
++ ret |= 1 << 12;
++ }
+
+ return ret;
+ }
+@@ -183,60 +436,139 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret, dword2;
+
+- ret = 0x00000;
+- ret |= MrsChipSel;
+-
+- /* program MrsAddress[1:0]=burst length and control method
+- (BL):based on F2x[1,0]84[BurstCtrl] */
+- dword = Get_NB32(dev, reg_off + 0x84);
+- ret |= dword & 3;
+-
+- /* program MrsAddress[3]=1 (BT):interleaved */
+- ret |= 1 << 3;
+-
+- /* program MrsAddress[6:4,2]=read CAS latency
+- (CL):based on F2x[1,0]88[Tcl] */
+- dword2 = Get_NB32(dev, reg_off + 0x88);
+- ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */
+- ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */
+-
+- /* program MrsAddress[12]=0 (PPD):slow exit */
+- if (dword & (1 << 23))
+- ret |= 1 << 12;
+-
+- /* program MrsAddress[11:9]=write recovery for auto-precharge
+- (WR):based on F2x[1,0]84[Twr] */
+- ret |= ((dword >> 4) & 7) << 9;
+-
+- /* program MrsAddress[8]=1 (DLL):DLL reset
+- just issue DLL reset at first time */
+- ret |= 1 << 8;
++ if (is_fam15h()) {
++ ret = 0x00000;
++ ret |= (MrsChipSel << 21);
++
++ /* Set defaults */
++ uint8_t ppd = 0;
++ uint8_t wr_ap = 0;
++ uint8_t dll_reset = 1;
++ uint8_t test_mode = 0;
++ uint8_t cas_latency = 0;
++ uint8_t read_burst_type = 1;
++ uint8_t burst_length = 0;
++
++ /* Obtain PchgPDModeSel */
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ ppd = (dword >> 23) & 0x1;
++
++ /* Obtain Twr */
++ dword = Get_NB32_DCT(dev, dct, 0x22c) & 0x1f;
++
++ /* Calculate wr_ap (Fam15h BKDG v3.14 Table 82) */
++ if (dword == 0x10)
++ wr_ap = 0x0;
++ else if (dword == 0x5)
++ wr_ap = 0x1;
++ else if (dword == 0x6)
++ wr_ap = 0x2;
++ else if (dword == 0x7)
++ wr_ap = 0x3;
++ else if (dword == 0x8)
++ wr_ap = 0x4;
++ else if (dword == 0xa)
++ wr_ap = 0x5;
++ else if (dword == 0xc)
++ wr_ap = 0x6;
++ else if (dword == 0xe)
++ wr_ap = 0x7;
++
++ /* Obtain Tcl */
++ dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;
++
++ /* Calculate cas_latency (Fam15h BKDG v3.14 Table 83) */
++ if (dword == 0x5)
++ cas_latency = 0x2;
++ else if (dword == 0x6)
++ cas_latency = 0x4;
++ else if (dword == 0x7)
++ cas_latency = 0x6;
++ else if (dword == 0x8)
++ cas_latency = 0x8;
++ else if (dword == 0x9)
++ cas_latency = 0xa;
++ else if (dword == 0xa)
++ cas_latency = 0xc;
++ else if (dword == 0xb)
++ cas_latency = 0xe;
++ else if (dword == 0xc)
++ cas_latency = 0x1;
++ else if (dword == 0xd)
++ cas_latency = 0x3;
++ else if (dword == 0xe)
++ cas_latency = 0x5;
++ else if (dword == 0xf)
++ cas_latency = 0x7;
++ else if (dword == 0x10)
++ cas_latency = 0x9;
++
++ /* Obtain BurstCtrl */
++ burst_length = Get_NB32_DCT(dev, dct, 0x84) & 0x3;
++
++ /* Load data into MRS word */
++ ret |= (ppd & 0x1) << 12;
++ ret |= (wr_ap & 0x3) << 9;
++ ret |= (dll_reset & 0x1) << 8;
++ ret |= (test_mode & 0x1) << 7;
++ ret |= ((cas_latency & 0xe) >> 1) << 4;
++ ret |= ((cas_latency & 0x1) >> 0) << 2;
++ ret |= (read_burst_type & 0x1) << 3;
++ ret |= (burst_length & 0x3);
++ } else {
++ ret = 0x00000;
++ ret |= (MrsChipSel << 20);
++
++ /* program MrsAddress[1:0]=burst length and control method
++ (BL):based on F2x[1,0]84[BurstCtrl] */
++ dword = Get_NB32_DCT(dev, dct, 0x84);
++ ret |= dword & 3;
++
++ /* program MrsAddress[3]=1 (BT):interleaved */
++ ret |= 1 << 3;
++
++ /* program MrsAddress[6:4,2]=read CAS latency
++ (CL):based on F2x[1,0]88[Tcl] */
++ dword2 = Get_NB32_DCT(dev, dct, 0x88);
++ ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */
++ ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */
++
++ /* program MrsAddress[12]=0 (PPD):slow exit */
++ if (dword & (1 << 23))
++ ret |= 1 << 12;
++
++ /* program MrsAddress[11:9]=write recovery for auto-precharge
++ (WR):based on F2x[1,0]84[Twr] */
++ ret |= ((dword >> 4) & 7) << 9;
++
++ /* program MrsAddress[8]=1 (DLL):DLL reset
++ just issue DLL reset at first time */
++ ret |= 1 << 8;
++ }
+
+ return ret;
+ }
+
+ static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword;
+
+ /*1.Program MrsAddress[10]=1
+ 2.Set SendZQCmd=1
+ */
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ dword = Get_NB32_DCT(dev, dct, 0x7C);
+ dword &= ~0xFFFFFF;
+ dword |= 1 << 10;
+ dword |= 1 << SendZQCmd;
+- Set_NB32(dev, reg_off + 0x7C, dword);
++ Set_NB32_DCT(dev, dct, 0x7C, dword);
+
+ /* Wait for SendZQCmd=0 */
+ do {
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ dword = Get_NB32_DCT(dev, dct, 0x7C);
+ } while (dword & (1 << SendZQCmd));
+
+ /* 4.Wait 512 MEMCLKs */
+@@ -248,31 +580,30 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ {
+ u8 MrsChipSel;
+ u32 dword;
+- u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+
+- if (pDCTstat->DIMMAutoSpeed == 4) {
++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+ /* 3.Program F2x[1,0]7C[EnDramInit]=1 */
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ dword = Get_NB32_DCT(dev, dct, 0x7c);
+ dword |= 1 << EnDramInit;
+- Set_NB32(dev, reg_off + 0x7C, dword);
++ Set_NB32_DCT(dev, dct, 0x7c, dword);
+ mct_DCTAccessDone(pDCTstat, dct);
+
+ /* 4.wait 200us */
+ mct_Wait(40000);
+
+- /* 5.On revision C processors, program F2x[1, 0]7C[DeassertMemRstX] = 1. */
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ /* 5.Program F2x[1, 0]7C[DeassertMemRstX] = 1. */
++ dword = Get_NB32_DCT(dev, dct, 0x7c);
+ dword |= 1 << DeassertMemRstX;
+- Set_NB32(dev, reg_off + 0x7C, dword);
++ Set_NB32_DCT(dev, dct, 0x7c, dword);
+
+ /* 6.wait 500us */
+ mct_Wait(200000);
+
+ /* 7.Program F2x[1,0]7C[AssertCke]=1 */
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ dword = Get_NB32_DCT(dev, dct, 0x7c);
+ dword |= 1 << AssertCke;
+- Set_NB32(dev, reg_off + 0x7C, dword);
++ Set_NB32_DCT(dev, dct, 0x7c, dword);
+
+ /* 8.wait 360ns */
+ mct_Wait(80);
+@@ -281,6 +612,13 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ * must be done for each chip select pair */
+ if (pDCTstat->Status & (1 << SB_Registered))
+ mct_DramControlReg_Init_D(pMCTstat, pDCTstat, dct);
++
++ /* The following steps are performed with load reduced DIMMs only and
++ * must be done for each DIMM */
++ // if (pDCTstat->Status & (1 << SB_LoadReduced))
++ /* TODO
++ * Implement LRDIMM configuration
++ */
+ }
+
+ /* The following steps are performed once for unbuffered DIMMs and once for each
+@@ -289,23 +627,23 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ u32 EMRS;
+ /* 13.Send EMRS(2) */
+- EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
++ EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel);
+ EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+ /* 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b */
+- EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
++ EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel);
+ EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+ /* 15.Send EMRS(1) */
+- EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
++ EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel);
+ EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+ /* 16.Send MRS with MrsAddress[8]=1(reset the DLL) */
+- EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
++ EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel);
+ EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+
+- if (pDCTstat->DIMMAutoSpeed == 4)
++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
+ if (!(pDCTstat->Status & (1 << SB_Registered)))
+ break; /* For UDIMM, only send MR commands once per channel */
+ }
+@@ -314,16 +652,15 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ MrsChipSel ++;
+ }
+
+- mct_Wait(100000);
+-
+- if (pDCTstat->DIMMAutoSpeed == 4) {
++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+ /* 17.Send two ZQCL commands */
+ mct_SendZQCmd(pDCTstat, dct);
+ mct_SendZQCmd(pDCTstat, dct);
++
+ /* 18.Program F2x[1,0]7C[EnDramInit]=0 */
+- dword = Get_NB32(dev, reg_off + 0x7C);
++ dword = Get_NB32_DCT(dev, dct, 0x7C);
+ dword &= ~(1 << EnDramInit);
+- Set_NB32(dev, reg_off + 0x7C, dword);
++ Set_NB32_DCT(dev, dct, 0x7C, dword);
+ mct_DCTAccessDone(pDCTstat, dct);
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 587c414..039fcf8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -23,7 +23,10 @@
+ Description: Receiver En and DQS Timing Training feature for DDR 3 MCT
+ ******************************************************************************/
+
+-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
++static int32_t abs(int32_t val);
++static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 Pass);
++static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass);
+ static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+@@ -32,7 +35,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel);
+ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 DQSRcvEnDly);
+-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
++static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat);
+
+@@ -89,11 +92,154 @@ static void SetupRcvrPattern(struct MCTStatStruc *pMCTstat,
+ void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass)
+ {
+- if(mct_checkNumberOfDqsRcvEn_1Pass(Pass))
+- dqsTrainRcvrEn_SW(pMCTstat, pDCTstat, Pass);
++ if(mct_checkNumberOfDqsRcvEn_1Pass(Pass)) {
++ if (is_fam15h())
++ dqsTrainRcvrEn_SW_Fam15(pMCTstat, pDCTstat, Pass);
++ else
++ dqsTrainRcvrEn_SW_Fam10(pMCTstat, pDCTstat, Pass);
++ }
+ }
+
+-static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++static uint16_t fam15_receiver_enable_training_seed(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
++{
++ uint32_t dword;
++ uint16_t seed = 0;
++
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ uint8_t channel = dct;
++ if (package_type == PT_GR) {
++ /* Get the internal node number */
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0xe8);
++ dword = (dword >> 30) & 0x3;
++ if (dword == 1) {
++ channel += 2;
++ }
++ }
++
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */
++ if (MaxDimmsInstallable == 1) {
++ if (channel == 0)
++ seed = 0x43;
++ else if (channel == 1)
++ seed = 0x3f;
++ else if (channel == 2)
++ seed = 0x3a;
++ else if (channel == 3)
++ seed = 0x35;
++ } else if (MaxDimmsInstallable == 2) {
++ if (channel == 0)
++ seed = 0x54;
++ else if (channel == 1)
++ seed = 0x4d;
++ else if (channel == 2)
++ seed = 0x45;
++ else if (channel == 3)
++ seed = 0x40;
++ } else if (MaxDimmsInstallable == 3) {
++ if (channel == 0)
++ seed = 0x6b;
++ else if (channel == 1)
++ seed = 0x5e;
++ else if (channel == 2)
++ seed = 0x4b;
++ else if (channel == 3)
++ seed = 0x3d;
++ }
++ } else if (package_type == PT_C3) {
++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */
++ if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) {
++ if (channel == 0)
++ seed = 0x3f;
++ else if (channel == 1)
++ seed = 0x3e;
++ } else if (MaxDimmsInstallable == 3) {
++ if (channel == 0)
++ seed = 0x47;
++ else if (channel == 1)
++ seed = 0x38;
++ }
++ }
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */
++ if (MaxDimmsInstallable == 1) {
++ if (channel == 0)
++ seed = 0x123;
++ else if (channel == 1)
++ seed = 0x122;
++ else if (channel == 2)
++ seed = 0x112;
++ else if (channel == 3)
++ seed = 0x102;
++ }
++ } else if (package_type == PT_C3) {
++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */
++ if (channel == 0)
++ seed = 0x132;
++ else if (channel == 1)
++ seed = 0x122;
++ }
++ } else {
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */
++ if (MaxDimmsInstallable == 1) {
++ if (channel == 0)
++ seed = 0x3e;
++ else if (channel == 1)
++ seed = 0x38;
++ else if (channel == 2)
++ seed = 0x37;
++ else if (channel == 3)
++ seed = 0x31;
++ } else if (MaxDimmsInstallable == 2) {
++ if (channel == 0)
++ seed = 0x51;
++ else if (channel == 1)
++ seed = 0x4a;
++ else if (channel == 2)
++ seed = 0x46;
++ else if (channel == 3)
++ seed = 0x3f;
++ } else if (MaxDimmsInstallable == 3) {
++ if (channel == 0)
++ seed = 0x5e;
++ else if (channel == 1)
++ seed = 0x52;
++ else if (channel == 2)
++ seed = 0x48;
++ else if (channel == 3)
++ seed = 0x3c;
++ }
++ } else if (package_type == PT_C3) {
++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */
++ if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) {
++ if (channel == 0)
++ seed = 0x39;
++ else if (channel == 1)
++ seed = 0x32;
++ } else if (MaxDimmsInstallable == 3) {
++ if (channel == 0)
++ seed = 0x45;
++ else if (channel == 1)
++ seed = 0x37;
++ }
++ } else if (package_type == PT_M2) {
++ /* Socket AM3: Fam15h BKDG v3.14 Table 101 */
++ seed = 0x3a;
++ }
++ }
++
++ return seed;
++}
++
++static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+ {
+ uint8_t lane;
+ uint32_t dword;
+@@ -111,7 +257,7 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela
+ if (lane == 8)
+ wdt_reg = 0x32;
+ wdt_reg += dimm * 3;
+- dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg);
+ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1))
+ current_total_delay[lane] = (dword & 0x00ff0000) >> 16;
+ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0))
+@@ -119,12 +265,124 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela
+ }
+ }
+
+-static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
++#ifdef UNUSED_CODE
++static void write_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t ret_reg;
++ if ((lane == 0) || (lane == 1))
++ ret_reg = 0x30;
++ if ((lane == 2) || (lane == 3))
++ ret_reg = 0x31;
++ if ((lane == 4) || (lane == 5))
++ ret_reg = 0x40;
++ if ((lane == 6) || (lane == 7))
++ ret_reg = 0x41;
++ if (lane == 8)
++ ret_reg = 0x32;
++ ret_reg += dimm * 3;
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
++ dword &= ~(0xff << 16);
++ dword |= (current_total_delay[lane] & 0xff) << 16;
++ }
++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
++ dword &= ~0xff;
++ dword |= current_total_delay[lane] & 0xff;
++ }
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword);
++ }
++}
++#endif
++
++static void write_write_data_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t wdt_reg;
++
++ /* Calculate Write Data Timing register location */
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ wdt_reg = 0x1;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ wdt_reg = 0x2;
++ if (lane == 8)
++ wdt_reg = 0x3;
++ wdt_reg |= (dimm << 8);
++
++ /* Set Write Data Timing register values */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg);
++ if ((lane == 7) || (lane == 3)) {
++ dword &= ~(0x7f << 24);
++ dword |= (current_total_delay[lane] & 0x7f) << 24;
++ }
++ if ((lane == 6) || (lane == 2)) {
++ dword &= ~(0x7f << 16);
++ dword |= (current_total_delay[lane] & 0x7f) << 16;
++ }
++ if ((lane == 5) || (lane == 1)) {
++ dword &= ~(0x7f << 8);
++ dword |= (current_total_delay[lane] & 0x7f) << 8;
++ }
++ if ((lane == 8) || (lane == 4) || (lane == 0)) {
++ dword &= ~0x7f;
++ dword |= current_total_delay[lane] & 0x7f;
++ }
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg, dword);
++ }
++}
++
++static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t mask;
++ uint32_t dword;
++
++ if (is_fam15h())
++ mask = 0x3ff;
++ else
++ mask = 0x1ff;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t ret_reg;
++ if ((lane == 0) || (lane == 1))
++ ret_reg = 0x10;
++ if ((lane == 2) || (lane == 3))
++ ret_reg = 0x11;
++ if ((lane == 4) || (lane == 5))
++ ret_reg = 0x20;
++ if ((lane == 6) || (lane == 7))
++ ret_reg = 0x21;
++ if (lane == 8)
++ ret_reg = 0x12;
++ ret_reg += dimm * 3;
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
++ current_total_delay[lane] = (dword & (mask << 16)) >> 16;
++ }
++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
++ current_total_delay[lane] = dword & mask;
++ }
++ }
++}
++
++static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+ {
+ uint8_t lane;
++ uint32_t mask;
+ uint32_t dword;
+
+- for (lane = 0; lane < 8; lane++) {
++ if (is_fam15h())
++ mask = 0x3ff;
++ else
++ mask = 0x1ff;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+ uint32_t ret_reg;
+ if ((lane == 0) || (lane == 1))
+ ret_reg = 0x10;
+@@ -134,17 +392,125 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_
+ ret_reg = 0x20;
+ if ((lane == 6) || (lane == 7))
+ ret_reg = 0x21;
++ if (lane == 8)
++ ret_reg = 0x12;
+ ret_reg += dimm * 3;
+- dword = Get_NB32_index_wait(dev, index_reg, ret_reg);
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
+ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
+- dword &= ~(0x1ff << 16);
+- dword |= (current_total_delay[lane] & 0x1ff) << 16;
++ dword &= ~(mask << 16);
++ dword |= (current_total_delay[lane] & mask) << 16;
+ }
+- if ((lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
+- dword &= ~0x1ff;
+- dword |= current_total_delay[lane] & 0x1ff;
++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
++ dword &= ~mask;
++ dword |= current_total_delay[lane] & mask;
+ }
+- Set_NB32_index_wait(dev, index_reg, ret_reg, dword);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword);
++ }
++}
++
++static void read_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t prc_reg;
++
++ /* Calculate DRAM Phase Recovery Control register location */
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ prc_reg = 0x50;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ prc_reg = 0x51;
++ if (lane == 8)
++ prc_reg = 0x52;
++
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg);
++ if ((lane == 7) || (lane == 3)) {
++ current_total_delay[lane] = (dword >> 24) & 0x7f;
++ }
++ if ((lane == 6) || (lane == 2)) {
++ current_total_delay[lane] = (dword >> 16) & 0x7f;
++ }
++ if ((lane == 5) || (lane == 1)) {
++ current_total_delay[lane] = (dword >> 8) & 0x7f;
++ }
++ if ((lane == 8) || (lane == 4) || (lane == 0)) {
++ current_total_delay[lane] = dword & 0x7f;
++ }
++ }
++}
++
++static void write_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t prc_reg;
++
++ /* Calculate DRAM Phase Recovery Control register location */
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ prc_reg = 0x50;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ prc_reg = 0x51;
++ if (lane == 8)
++ prc_reg = 0x52;
++
++ /* Set DRAM Phase Recovery Control register values */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg);
++ if ((lane == 7) || (lane == 3)) {
++ dword &= ~(0x7f << 24);
++ dword |= (current_total_delay[lane] & 0x7f) << 24;
++ }
++ if ((lane == 6) || (lane == 2)) {
++ dword &= ~(0x7f << 16);
++ dword |= (current_total_delay[lane] & 0x7f) << 16;
++ }
++ if ((lane == 5) || (lane == 1)) {
++ dword &= ~(0x7f << 8);
++ dword |= (current_total_delay[lane] & 0x7f) << 8;
++ }
++ if ((lane == 8) || (lane == 4) || (lane == 0)) {
++ dword &= ~0x7f;
++ dword |= current_total_delay[lane] & 0x7f;
++ }
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg, dword);
++ }
++}
++
++static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
++{
++ uint8_t lane;
++ uint32_t dword;
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint32_t rdt_reg;
++
++ /* Calculate DRAM Read DQS Timing register location */
++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
++ rdt_reg = 0x5;
++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
++ rdt_reg = 0x6;
++ if (lane == 8)
++ rdt_reg = 0x7;
++ rdt_reg |= (dimm << 8);
++
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, rdt_reg);
++ if ((lane == 7) || (lane == 3)) {
++ current_total_delay[lane] = (dword >> 24) & 0x3f;
++ }
++ if ((lane == 6) || (lane == 2)) {
++ current_total_delay[lane] = (dword >> 16) & 0x3f;
++ }
++ if ((lane == 5) || (lane == 1)) {
++ current_total_delay[lane] = (dword >> 8) & 0x3f;
++ }
++ if ((lane == 8) || (lane == 4) || (lane == 0)) {
++ current_total_delay[lane] = dword & 0x3f;
++ }
++
++ if (is_fam15h())
++ current_total_delay[lane] >>= 1;
+ }
+ }
+
+@@ -160,10 +526,11 @@ static uint32_t convert_testaddr_and_channel_to_address(struct DCTStatStruc *pDC
+ return testaddr;
+ }
+
+-/* DQS Receiver Enable Training
+- * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.2
++/* DQS Receiver Enable Training (Family 10h)
++ * Algorithm detailed in:
++ * The Fam10h BKDG Rev. 3.62 section 2.8.9.9.2
+ */
+-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
++static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass)
+ {
+ u8 Channel;
+@@ -171,7 +538,6 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ u8 Addl_Index = 0;
+ u8 Receiver;
+ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
+- u8 Final_Value;
+ u16 CTLRMaxDelay;
+ u16 MaxDelay_CH[2];
+ u32 TestAddr0, TestAddr1, TestAddr0B, TestAddr1B;
+@@ -188,6 +554,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ u32 lo, hi;
+
+ uint32_t dword;
++ uint8_t dimm;
+ uint8_t rank;
+ uint8_t lane;
+ uint16_t current_total_delay[MAX_BYTE_LANES];
+@@ -214,14 +581,13 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ }
+
+ for (ch = ch_start; ch < ch_end; ch++) {
+- reg = 0x78 + (0x100 * ch);
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, ch, reg);
+ val &= ~(0x3ff << 22);
+- val |= (0x0c8 << 22); /* Max Rd Lat */
+- Set_NB32(dev, reg, val);
++ val |= (0x0c8 << 22); /* MaxRdLatency = 0xc8 */
++ Set_NB32_DCT(dev, ch, reg, val);
+ }
+
+- Final_Value = 1;
+ if (Pass == FirstPass) {
+ mct_InitDQSPos4RcvrEn_D(pMCTstat, pDCTstat);
+ } else {
+@@ -260,7 +626,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+
+ CTLRMaxDelay = 0;
+ MaxDelay_CH[Channel] = 0;
+- index_reg = 0x98 + 0x100 * Channel;
++ index_reg = 0x98;
+
+ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
+ /* There are four receiver pairs, loosely associated with chipselects.
+@@ -268,6 +634,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ */
+ for (; Receiver < 8; Receiver += 2) {
+ Addl_Index = (Receiver >> 1) * 3 + 0x10;
++ dimm = (Receiver >> 1);
+
+ print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
+
+@@ -284,45 +651,14 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ /* 2.8.9.9.2 (1, 6)
+ * Retrieve gross and fine timing fields from write DQS registers
+ */
+- read_dqs_write_timing_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+ /* 2.8.9.9.2 (1)
+ * Program the Write Data Timing and Write ECC Timing register to
+ * the values stored in the DQS Write Timing Control register
+ * for each lane
+ */
+- for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+- uint32_t wdt_reg;
+-
+- /* Calculate Write Data Timing register location */
+- if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
+- wdt_reg = 0x1;
+- if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
+- wdt_reg = 0x2;
+- if (lane == 8)
+- wdt_reg = 0x3;
+- wdt_reg |= ((Receiver / 2) << 8);
+-
+- /* Set Write Data Timing register values */
+- dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
+- if ((lane == 7) || (lane == 3)) {
+- dword &= ~(0x7f << 24);
+- dword |= (current_total_delay[lane] & 0x7f) << 24;
+- }
+- if ((lane == 6) || (lane == 2)) {
+- dword &= ~(0x7f << 16);
+- dword |= (current_total_delay[lane] & 0x7f) << 16;
+- }
+- if ((lane == 5) || (lane == 1)) {
+- dword &= ~(0x7f << 8);
+- dword |= (current_total_delay[lane] & 0x7f) << 8;
+- }
+- if ((lane == 8) || (lane == 4) || (lane == 0)) {
+- dword &= ~0x7f;
+- dword |= current_total_delay[lane] & 0x7f;
+- }
+- Set_NB32_index_wait(dev, index_reg, wdt_reg, dword);
+- }
++ write_write_data_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+ /* 2.8.9.9.2 (2)
+ * Program the Read DQS Timing Control and the Read DQS ECC Timing Control registers
+@@ -336,12 +672,12 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ rdt_reg = 0x6;
+ if (lane == 8)
+ rdt_reg = 0x7;
+- rdt_reg |= ((Receiver / 2) << 8);
++ rdt_reg |= (dimm << 8);
+ if (lane == 8)
+ dword = 0x0000003f;
+ else
+ dword = 0x3f3f3f3f;
+- Set_NB32_index_wait(dev, index_reg, rdt_reg, dword);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, rdt_reg, dword);
+ }
+
+ /* 2.8.9.9.2 (3)
+@@ -371,7 +707,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr1B ", TestAddr1B, 2);
+
+ /* 2.8.9.9.2 (4, 5)
+- * Write 1 cache line of the appropriate test pattern to each test addresse
++ * Write 1 cache line of the appropriate test pattern to each test address
+ */
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, 0); /* rank 0 of DIMM, testpattern 0 */
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, 1); /* rank 0 of DIMM, testpattern 1 */
+@@ -390,7 +726,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ /* 2.8.9.9.2 (6)
+ * Write gross and fine timing fields to read DQS registers
+ */
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+ /* 2.8.9.9.2 (7)
+ * Loop over all delay values up to 1 MEMCLK (0x40 delay steps) from the initial delay values
+@@ -417,8 +753,8 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ break;
+
+ /* 2.8.9.9.2 (7 A)
+- * Loop over all ranks
+- */
++ * Loop over all ranks
++ */
+ for (rank = 0; rank < (_2Ranks + 1); rank++) {
+ /* 2.8.9.9.2 (7 A a-d)
+ * Read the first test address of the current rank
+@@ -434,17 +770,17 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ */
+ proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
+ result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+ proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
+ result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+ } else {
+ proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
+ result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+ proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
+ result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+ }
+ /* 2.8.9.9.2 (7 A e)
+ * Compare both read patterns and flag passing ranks/lanes
+@@ -534,7 +870,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ }
+
+ /* Update delays in hardware */
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+ /* Save previous results for comparison in the next iteration */
+ for (lane = 0; lane < 8; lane++)
+@@ -588,7 +924,483 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ mct_SetMaxLatency_D(pDCTstat, Channel, CTLRMaxDelay); /* program Ch A/B MaxAsyncLat to correspond with max delay */
+ }
+
+- ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
++ for (Channel = 0; Channel < 2; Channel++) {
++ ResetDCTWrPtr_D(dev, Channel, index_reg, Addl_Index);
++ }
++
++ if(_DisableDramECC) {
++ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
++ }
++
++ if (Pass == FirstPass) {
++ /*Disable DQSRcvrEn training mode */
++ mct_DisableDQSRcvEn_D(pDCTstat);
++ }
++
++ if(!_Wrap32Dis) {
++ msr = HWCR;
++ _RDMSR(msr, &lo, &hi);
++ lo &= ~(1<<17); /* restore HWCR.wrap32dis */
++ _WRMSR(msr, lo, hi);
++ }
++ if(!_SSE2){
++ cr4 = read_cr4();
++ cr4 &= ~(1<<9); /* restore cr4.OSFXSR */
++ write_cr4(cr4);
++ }
++
++#if DQS_TRAIN_DEBUG > 0
++ {
++ u8 ChannelDTD;
++ printk(BIOS_DEBUG, "TrainRcvrEn: CH_MaxRdLat:\n");
++ for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel:%x: %x\n",
++ ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
++ }
++ }
++#endif
++
++#if DQS_TRAIN_DEBUG > 0
++ {
++ u16 valDTD;
++ u8 ChannelDTD, ReceiverDTD;
++ u8 i;
++ u16 *p;
++
++ printk(BIOS_DEBUG, "TrainRcvrEn: CH_D_B_RCVRDLY:\n");
++ for(ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel:%x\n", ChannelDTD);
++ for(ReceiverDTD = 0; ReceiverDTD<8; ReceiverDTD+=2) {
++ printk(BIOS_DEBUG, "\t\tReceiver:%x:", ReceiverDTD);
++ p = pDCTstat->CH_D_B_RCVRDLY[ChannelDTD][ReceiverDTD>>1];
++ for (i=0;i<8; i++) {
++ valDTD = p[i];
++ printk(BIOS_DEBUG, " %03x", valDTD);
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
++ }
++ }
++#endif
++
++ printk(BIOS_DEBUG, "TrainRcvrEn: Status %x\n", pDCTstat->Status);
++ printk(BIOS_DEBUG, "TrainRcvrEn: ErrStatus %x\n", pDCTstat->ErrStatus);
++ printk(BIOS_DEBUG, "TrainRcvrEn: ErrCode %x\n", pDCTstat->ErrCode);
++ printk(BIOS_DEBUG, "TrainRcvrEn: Done\n\n");
++}
++
++/* DQS Receiver Enable Training Pattern Generation (Family 15h)
++ * Algorithm detailed in:
++ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2 (4)
++ */
++static void generate_dram_receiver_enable_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
++{
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ /* 2.10.5.7.1.1
++ * It appears that the DCT only supports 8-beat burst length mode,
++ * so do nothing here...
++ */
++
++ /* Wait for CmdSendInProg == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ } while (dword & (0x1 << 12));
++
++ /* Set CmdTestEnable = 1 */
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword |= (0x1 << 2);
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.1 Send Activate Command */
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */
++ dword |= ((0x1 << Receiver) << 22);
++ dword &= ~(0x7 << 19); /* CmdBank = 0 */
++ dword &= ~(0x3ffff); /* CmdAddress = 0 */
++ dword |= (0x1 << 31); /* SendActCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x28c, dword);
++
++ /* Wait for SendActCmd == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ } while (dword & (0x1 << 31));
++
++ /* Wait 75 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
++
++ /* 2.10.5.8.6.1.2 */
++ Set_NB32_DCT(dev, dct, 0x274, 0x0); /* DQMask = 0 */
++ Set_NB32_DCT(dev, dct, 0x278, 0x0);
++
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0 */
++ if (pDCTstat->DimmECCPresent == 0)
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
++
++ /* 2.10.5.8.6.1.2 */
++ dword = Get_NB32_DCT(dev, dct, 0x270);
++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */
++// dword |= (0x55555);
++ dword |= (0x44443); /* Use AGESA seed */
++ Set_NB32_DCT(dev, dct, 0x270, dword);
++
++ /* 2.10.5.8.2 (4) */
++ dword = Get_NB32_DCT(dev, dct, 0x260);
++ dword &= ~(0x1fffff); /* CmdCount = 192 */
++ dword |= 192;
++ Set_NB32_DCT(dev, dct, 0x260, dword);
++
++#if 0
++ /* TODO: This applies to Fam15h model 10h and above only */
++ /* Program Bubble Count and CmdStreamLen */
++ dword = Get_NB32_DCT(dev, dct, 0x25c);
++ dword &= ~(0x3ff << 12); /* BubbleCnt = 0 */
++ dword &= ~(0x3ff << 22); /* BubbleCnt2 = 0 */
++ dword &= ~(0xff); /* CmdStreamLen = 1 */
++ dword |= 0x1;
++ Set_NB32_DCT(dev, dct, 0x25c, dword);
++#endif
++
++ /* Configure Target A */
++ dword = Get_NB32_DCT(dev, dct, 0x254);
++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */
++ dword |= (Receiver & 0x7) << 24;
++ dword &= ~(0x7 << 21); /* TgtBank = 0 */
++ dword &= ~(0x3ff); /* TgtAddress = 0 */
++ Set_NB32_DCT(dev, dct, 0x254, dword);
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword |= (0x1 << 3); /* ResetAllErr = 1 */
++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */
++ dword &= ~(0x3 << 8); /* CmdTgt = 0 (Target A) */
++ dword &= ~(0x7 << 5); /* CmdType = 0 (Read) */
++ dword |= (0x1 << 11); /* SendCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
++
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword &= ~(0x1 << 11); /* SendCmd = 0 */
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++
++ /* 2.10.5.8.6.1.1 Send Precharge Command */
++ /* Wait 25 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
++
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */
++ dword |= ((0x1 << Receiver) << 22);
++ dword &= ~(0x7 << 19); /* CmdBank = 0 */
++ dword &= ~(0x3ffff); /* CmdAddress = 0x400 */
++ dword |= 0x400;
++ dword |= (0x1 << 30); /* SendPchgCmd = 1 */
++ Set_NB32_DCT(dev, dct, 0x28c, dword);
++
++ /* Wait for SendPchgCmd == 0 */
++ do {
++ dword = Get_NB32_DCT(dev, dct, 0x28c);
++ } while (dword & (0x1 << 30));
++
++ /* Wait 25 MEMCLKs. */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
++
++ /* Set CmdTestEnable = 0 */
++ dword = Get_NB32_DCT(dev, dct, 0x250);
++ dword &= ~(0x1 << 2);
++ Set_NB32_DCT(dev, dct, 0x250, dword);
++}
++
++/* DQS Receiver Enable Training (Family 15h)
++ * Algorithm detailed in:
++ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2
++ * This algorithm runs once at the lowest supported MEMCLK,
++ * then once again at the highest supported MEMCLK.
++ */
++static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, u8 Pass)
++{
++ u8 Channel;
++ u8 _2Ranks;
++ u8 Addl_Index = 0;
++ u8 Receiver;
++ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
++ u32 Errors;
++
++ u32 val;
++ u32 dev;
++ u32 index_reg;
++ u32 ch_start, ch_end, ch;
++ u32 msr;
++ u32 cr4;
++ u32 lo, hi;
++
++ uint32_t dword;
++ uint8_t dimm;
++ uint8_t rank;
++ uint8_t lane;
++ uint8_t mem_clk;
++ uint16_t initial_seed;
++ uint16_t current_total_delay[MAX_BYTE_LANES];
++ uint16_t dqs_ret_pass1_total_delay[MAX_BYTE_LANES];
++ uint16_t rank0_current_total_delay[MAX_BYTE_LANES];
++ uint16_t phase_recovery_delays[MAX_BYTE_LANES];
++ uint16_t seed[MAX_BYTE_LANES];
++ uint16_t seed_gross[MAX_BYTE_LANES];
++ uint16_t seed_fine[MAX_BYTE_LANES];
++ uint16_t seed_pre_gross[MAX_BYTE_LANES];
++
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++
++ print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
++ print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
++
++ dev = pDCTstat->dev_dct;
++ index_reg = 0x98;
++ ch_start = 0;
++ ch_end = 2;
++
++ for (ch = ch_start; ch < ch_end; ch++) {
++ uint8_t max_rd_latency = 0x55;
++ uint8_t p_state;
++
++ /* 2.10.5.6 */
++ fam15EnableTrainingMode(pMCTstat, pDCTstat, ch, 1);
++
++ /* 2.10.5.2 */
++ for (p_state = 0; p_state < 3; p_state++) {
++ val = Get_NB32_DCT_NBPstate(dev, ch, p_state, 0x210);
++ val &= ~(0x3ff << 22); /* MaxRdLatency = max_rd_latency */
++ val |= (max_rd_latency & 0x3ff) << 22;
++ Set_NB32_DCT_NBPstate(dev, ch, p_state, 0x210, val);
++ }
++ }
++
++ if (Pass != FirstPass) {
++ pDCTstat->DimmTrainFail = 0;
++ pDCTstat->CSTrainFail = ~pDCTstat->CSPresent;
++ }
++
++ cr4 = read_cr4();
++ if(cr4 & ( 1 << 9)) { /* save the old value */
++ _SSE2 = 1;
++ }
++ cr4 |= (1 << 9); /* OSFXSR enable SSE2 */
++ write_cr4(cr4);
++
++ msr = HWCR;
++ _RDMSR(msr, &lo, &hi);
++ /* FIXME: Why use SSEDIS */
++ if(lo & (1 << 17)) { /* save the old value */
++ _Wrap32Dis = 1;
++ }
++ lo |= (1 << 17); /* HWCR.wrap32dis */
++ lo &= ~(1 << 15); /* SSEDIS */
++ _WRMSR(msr, lo, hi); /* Setting wrap32dis allows 64-bit memory references in real mode */
++
++ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
++
++ Errors = 0;
++ dev = pDCTstat->dev_dct;
++
++ for (Channel = 0; Channel < 2; Channel++) {
++ print_debug_dqs("\tTrainRcvEn51: Node ", pDCTstat->Node_ID, 1);
++ print_debug_dqs("\tTrainRcvEn51: Channel ", Channel, 1);
++ pDCTstat->Channel = Channel;
++
++ mem_clk = Get_NB32_DCT(dev, Channel, 0x94) & 0x1f;
++
++ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each DIMM.
++ */
++ for (; Receiver < 8; Receiver += 2) {
++ Addl_Index = (Receiver >> 1) * 3 + 0x10;
++ dimm = (Receiver >> 1);
++
++ print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
++ continue;
++ }
++
++ /* Retrieve the total delay values from pass 1 of DQS receiver enable training */
++ if (Pass != FirstPass) {
++ read_dqs_receiver_enable_control_registers(dqs_ret_pass1_total_delay, dev, Channel, dimm, index_reg);
++ }
++
++ /* 2.10.5.8.2
++ * Loop over all ranks
++ */
++ if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1))
++ _2Ranks = 1;
++ else
++ _2Ranks = 0;
++ for (rank = 0; rank < (_2Ranks + 1); rank++) {
++ /* 2.10.5.8.2 (1)
++ * Specify the target DIMM to be trained
++ * Set TrNibbleSel = 0
++ *
++ * TODO: Add support for x4 DIMMs
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword &= ~(0x3 << 4); /* TrDimmSel */
++ dword |= ((dimm & 0x3) << 4);
++ dword &= ~(0x1 << 2); /* TrNibbleSel */
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++
++ /* 2.10.5.8.2 (2)
++ * Retrieve gross and fine timing fields from write DQS registers
++ */
++ read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++
++ /* 2.10.5.8.2.1
++ * Generate the DQS Receiver Enable Training Seed Values
++ */
++ if (Pass == FirstPass) {
++ initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
++
++ /* Adjust seed for the minimum platform supported frequency */
++ initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
++ fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint16_t wl_pass1_delay;
++ wl_pass1_delay = current_total_delay[lane];
++
++ seed[lane] = initial_seed + wl_pass1_delay;
++ }
++ } else {
++ uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++ uint16_t register_delay;
++ int16_t seed_prescaling;
++
++ memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
++ if ((pDCTstat->Status & (1 << SB_Registered))) {
++ if (addr_prelaunch)
++ register_delay = 0x30;
++ else
++ register_delay = 0x20;
++ } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
++ /* TODO
++ * Load reduced DIMM support unimplemented
++ */
++ register_delay = 0x0;
++ } else {
++ register_delay = 0x0;
++ }
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ seed_prescaling = current_total_delay[lane] - register_delay - 0x20;
++ seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++ }
++ }
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
++ seed_fine[lane] = seed[lane] & 0x1f;
++
++ /*if (seed_gross[lane] == 0)
++ seed_pre_gross[lane] = 0;
++ else */if (seed_gross[lane] & 0x1)
++ seed_pre_gross[lane] = 1;
++ else
++ seed_pre_gross[lane] = 2;
++
++ /* Calculate phase recovery delays */
++ phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
++
++ /* Set the gross delay.
++ * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
++ * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
++ */
++ current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
++ }
++
++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
++ * Program PhRecFineDly and PhRecGrossDly
++ */
++ write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
++
++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7)
++ * Program the DQS Receiver Enable delay values for each lane
++ */
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++
++ /* 2.10.5.8.2 (3)
++ * Program DqsRcvTrEn = 1
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword |= (0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++
++ /* 2.10.5.8.2 (4)
++ * Issue 192 read requests to the target rank
++ */
++ generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
++
++ /* 2.10.5.8.2 (5)
++ * Program DqsRcvTrEn = 0
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword &= ~(0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++
++ /* 2.10.5.8.2 (6)
++ * Read PhRecGrossDly, PhRecFineDly
++ */
++ read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
++
++ /* 2.10.5.8.2 (7)
++ * Calculate and program the DQS Receiver Enable delay values
++ */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
++ current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
++ if (lane == 8)
++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
++ else
++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
++ }
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++
++ if (rank == 0) {
++ /* Back up the Rank 0 delays for later use */
++ memcpy(rank0_current_total_delay, current_total_delay, sizeof(current_total_delay));
++ }
++
++ if (rank == 1) {
++ /* 2.10.5.8.2 (8)
++ * Compute the average delay across both ranks and program the result into
++ * the DQS Receiver Enable delay registers
++ */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ current_total_delay[lane] = (rank0_current_total_delay[lane] + current_total_delay[lane]) / 2;
++ if (lane == 8)
++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
++ else
++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
++ }
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++ }
++ }
++
++#if DQS_TRAIN_DEBUG > 0
++ for (lane = 0; lane < 8; lane++)
++ print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
++#endif
++ }
++ }
++
++ /* Calculate and program MaxRdLatency */
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel);
+
+ if(_DisableDramECC) {
+ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
+@@ -675,10 +1487,10 @@ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat)
+ }
+
+ for (ch=0; ch<ch_end; ch++) {
+- reg = 0x78 + 0x100 * ch;
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, ch, reg);
+ val &= ~(1 << DqsRcvEnTrain);
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, ch, reg, val);
+ }
+ }
+
+@@ -719,7 +1531,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly,
+ /* get the register index from table */
+ index = Table_DQSRcvEn_Offset[i >> 1];
+ index += Addl_Index; /* DIMMx DqsRcvEn byte0 */
+- val = Get_NB32_index_wait(dev, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, index);
+ if(i & 1) {
+ /* odd byte lane */
+ val &= ~(0x1ff << 16);
+@@ -729,7 +1541,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly,
+ val &= ~0x1ff;
+ val |= (RcvrEnDly & 0x1ff);
+ }
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val);
+ }
+
+ }
+@@ -743,7 +1555,6 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+ u32 reg;
+ u32 SubTotal;
+ u32 index_reg;
+- u32 reg_off;
+ u32 val;
+
+ uint8_t cpu_val_n;
+@@ -778,17 +1589,16 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+ Channel = 0;
+
+ dev = pDCTstat->dev_dct;
+- reg_off = 0x100 * Channel;
+- index_reg = 0x98 + reg_off;
++ index_reg = 0x98;
+
+ /* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/
+- val = Get_NB32(dev, 0x88 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x88);
+ SubTotal = ((val & 0x0f) + 4) << 1; /* SubTotal is 1/2 Memclk unit */
+
+ /* If registered DIMMs are being used then
+ * add 1 MEMCLK to the sub-total.
+ */
+- val = Get_NB32(dev, 0x90 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x90);
+ if(!(val & (1 << UnBuffDimm)))
+ SubTotal += 2;
+
+@@ -796,7 +1606,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+ * add 1, else add 2 to the sub-total.
+ * if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
+ */
+- val = Get_NB32_index_wait(dev, index_reg, 0x04);
++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04);
+ if(!(val & 0x00202020))
+ SubTotal += 1;
+ else
+@@ -804,7 +1614,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+
+ /* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
+ * then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */
+- val = Get_NB32(dev, 0x78 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x78);
+ SubTotal += 8 - (val & 0x0f);
+
+ /* Convert bits 7-5 (also referred to as the coarse delay) of
+@@ -825,7 +1635,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+ * clocks (NCLKs)
+ */
+ SubTotal *= 200 * ((Get_NB32(pDCTstat->dev_nbmisc, 0xd4) & 0x1f) + 4);
+- SubTotal /= freq_tab[((Get_NB32(pDCTstat->dev_dct, 0x94 + reg_off) & 0x7) - 3)];
++ SubTotal /= freq_tab[((Get_NB32_DCT(pDCTstat->dev_dct, Channel, 0x94) & 0x7) - 3)];
+ SubTotal = (SubTotal + (2 - 1)) / 2; /* Round up */
+
+ /* Add "N" NCLKs to the sub-total. "N" represents part of the
+@@ -842,13 +1652,13 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
+ /* Program the F2x[1, 0]78[MaxRdLatency] register with
+ * the total delay value (in NCLKs).
+ */
+- reg = 0x78 + reg_off;
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, Channel, reg);
+ val &= ~(0x3ff << 22);
+ val |= (SubTotal & 0x3ff) << 22;
+
+ /* program MaxRdLatency to correspond with current delay */
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, Channel, reg, val);
+ }
+
+ static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+@@ -878,7 +1688,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ u32 dword;
+ u8 dn = 4; /* TODO: Rev C could be 4 */
+ u32 dev = pDCTstat->dev_dct;
+- u32 index_reg = 0x98 + 0x100 * Channel;
++ u32 index_reg = 0x98;
+
+ /* FIXME: add Cx support */
+ dword = 0x00000000;
+@@ -886,7 +1696,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ for(j=0; j<dn; j++)
+ /* DIMM0 Write Data Timing Low */
+ /* DIMM0 Write ECC Timing */
+- Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword);
+ }
+
+ /* errata #180 */
+@@ -894,13 +1704,13 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ for(i=5; i<=6; i++) {
+ for(j=0; j<dn; j++)
+ /* DIMM0 Read DQS Timing Control Low */
+- Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword);
+ }
+
+ dword = 0x0000002f;
+ for(j=0; j<dn; j++)
+ /* DIMM0 Read DQS ECC Timing Control */
+- Set_NB32_index_wait(dev, index_reg, 7 + 0x100 * j, dword);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 7 + 0x100 * j, dword);
+ }
+
+ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
+@@ -913,13 +1723,13 @@ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+- index_reg = 0x98 + Channel * 0x100;
++ index_reg = 0x98;
+ index = 0x12;
+ p = pDCTstat->CH_D_BC_RCVRDLY[Channel];
+ print_debug_dqs("\t\tSetEccDQSRcvrPos: Channel ", Channel, 2);
+ for(ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+ val = p[ChipSel>>1];
+- Set_NB32_index_wait(dev, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val);
+ print_debug_dqs_pair("\t\tSetEccDQSRcvrPos: ChipSel ",
+ ChipSel, " rcvr_delay ", val, 2);
+ index += 3;
+@@ -1003,95 +1813,305 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+ u8 Node = 0;
+ struct DCTStatStruc *pDCTstat;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* FIXME: skip for Ax */
+- while (Node < MAX_NODES_SUPPORTED) {
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
++ if (!pDCTstat->NodePresent)
++ continue;
++
++ if (pDCTstat->DCTSysLimit) {
++ if (is_fam15h()) {
++ /* Fam15h BKDG v3.14 section 2.10.5.3.3
++ * This picks up where InitDDRPhy left off
++ */
++ uint8_t dct;
++ uint8_t index;
++ uint32_t dword;
++ uint32_t datc_backup;
++ uint32_t training_dword;
++ uint32_t fence2_config_dword;
++ uint32_t fence_tx_pad_config_dword;
++ uint32_t index_reg = 0x98;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ for (dct = 0; dct < 2; dct++) {
++ if (!pDCTstat->DIMMValidDCT[dct])
++ continue;
++
++ /* Back up D18F2x9C_x0000_0004_dct[1:0] */
++ datc_backup = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
++
++ /* FenceTrSel = 0x2 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
++ dword &= ~(0x3 << 6);
++ dword |= (0x2 << 6);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
++
++ /* Set phase recovery seed values */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
++
++ training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
++
++ /* Save calculated fence value to the TX DLL */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ dword &= ~(0x1f << 26);
++ dword |= ((training_dword & 0x1f) << 26);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
++
++ /* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x1 */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8));
++ dword &= ~(0x7 << 12);
++ dword |= (0x1 << 12);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword);
++ }
++
++ /* FenceTrSel = 0x1 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
++ dword &= ~(0x3 << 6);
++ dword |= (0x1 << 6);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
++
++ /* Set phase recovery seed values */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
++
++ training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
++
++ /* Save calculated fence value to the RX DLL */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ dword &= ~(0x1f << 21);
++ dword |= ((training_dword & 0x1f) << 21);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
++
++ /* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x0 */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8));
++ dword &= ~(0x7 << 12);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword);
++ }
++
++ /* FenceTrSel = 0x3 */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
++ dword &= ~(0x3 << 6);
++ dword |= (0x3 << 6);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
++
++ /* Set phase recovery seed values */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
++
++ fence_tx_pad_config_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
++
++ /* Save calculated fence value to the TX Pad */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ dword &= ~(0x1f << 16);
++ dword |= ((fence_tx_pad_config_dword & 0x1f) << 16);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
++
++ /* Program D18F2x9C_x0D0F_[C,8,2][2:0]31_dct[1:0] */
++ training_dword = fence_tx_pad_config_dword;
++ if (fence_tx_pad_config_dword < 16)
++ training_dword |= (0x1 << 4);
++ else
++ training_dword = 0;
++ for (index = 0; index < 0x3; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8));
++ dword &= ~(0x1f);
++ dword |= (training_dword & 0x1f);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8), dword);
++ }
++ for (index = 0; index < 0x3; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8));
++ dword &= ~(0x1f);
++ dword |= (training_dword & 0x1f);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8), dword);
++ }
++ for (index = 0; index < 0x3; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8));
++ dword &= ~(0x1f);
++ dword |= (training_dword & 0x1f);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8), dword);
++ }
++
++ /* Assemble Fence2 configuration word (Fam15h BKDG v3.14 page 331) */
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ fence2_config_dword = 0;
++
++ /* TxPad */
++ training_dword = (dword >> 16) & 0x1f;
++ if (training_dword < 16)
++ training_dword |= 0x10;
++ else
++ training_dword = 0;
++ fence2_config_dword |= training_dword;
++
++ /* RxDll */
++ training_dword = (dword >> 21) & 0x1f;
++ if (training_dword < 16)
++ training_dword |= 0x10;
++ else
++ training_dword = 0;
++ fence2_config_dword |= (training_dword << 10);
++
++ /* TxDll */
++ training_dword = (dword >> 26) & 0x1f;
++ if (training_dword < 16)
++ training_dword |= 0x10;
++ else
++ training_dword = 0;
++ fence2_config_dword |= (training_dword << 5);
++
++ /* Program D18F2x9C_x0D0F_0[F,8:0]31_dct[1:0] */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8));
++ dword &= ~(0x7fff);
++ dword |= fence2_config_dword;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8), dword);
++ }
+
+- if(pDCTstat->DCTSysLimit) {
+- fenceDynTraining_D(pMCTstat, pDCTstat, 0);
+- fenceDynTraining_D(pMCTstat, pDCTstat, 1);
++ /* Restore D18F2x9C_x0000_0004_dct[1:0] */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004, datc_backup);
++ }
++ } else {
++ fenceDynTraining_D(pMCTstat, pDCTstat, 0);
++ fenceDynTraining_D(pMCTstat, pDCTstat, 1);
++ }
+ }
+- Node++;
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
++static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u16 avRecValue;
+ u32 val;
+ u32 dev;
+- u32 index_reg = 0x98 + 0x100 * dct;
++ u32 index_reg = 0x98;
+ u32 index;
+
+- /* BIOS first programs a seed value to the phase recovery engine
+- * (recommended 19) registers.
+- * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and
+- * F2x[1,0]9C_x52.) .
+- */
+ dev = pDCTstat->dev_dct;
+- for (index = 0x50; index <= 0x52; index ++) {
+- val = (FenceTrnFinDlySeed & 0x1F);
+- if (index != 0x52) {
+- val |= val << 8 | val << 16 | val << 24;
++
++ if (is_fam15h()) {
++ /* Set F2x[1,0]9C_x08[PhyFenceTrEn] */
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
++ val |= 1 << PhyFenceTrEn;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
++
++ /* Wait 2000 MEMCLKs */
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 2000);
++
++ /* Clear F2x[1,0]9C_x08[PhyFenceTrEn] */
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
++ val &= ~(1 << PhyFenceTrEn);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
++
++ /* BIOS reads the phase recovery engine registers
++ * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52.
++ * Average the fine delay components only.
++ */
++ avRecValue = 0;
++ for (index = 0x50; index <= 0x52; index++) {
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++ avRecValue += val & 0x1f;
++ if (index != 0x52) {
++ avRecValue += (val >> 8) & 0x1f;
++ avRecValue += (val >> 16) & 0x1f;
++ avRecValue += (val >> 24) & 0x1f;
++ }
+ }
+- Set_NB32_index_wait(dev, index_reg, index, val);
+- }
+
+- /* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */
+- val = Get_NB32_index_wait(dev, index_reg, 0x08);
+- val |= 1 << PhyFenceTrEn;
+- Set_NB32_index_wait(dev, index_reg, 0x08, val);
+-
+- /* Wait 200 MEMCLKs. */
+- mct_Wait(50000); /* wait 200us */
+-
+- /* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */
+- val = Get_NB32_index_wait(dev, index_reg, 0x08);
+- val &= ~(1 << PhyFenceTrEn);
+- Set_NB32_index_wait(dev, index_reg, 0x08, val);
+-
+- /* BIOS reads the phase recovery engine registers
+- * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */
+- avRecValue = 0;
+- for (index = 0x50; index <= 0x52; index ++) {
+- val = Get_NB32_index_wait(dev, index_reg, index);
+- avRecValue += val & 0x7F;
+- if (index != 0x52) {
+- avRecValue += (val >> 8) & 0x7F;
+- avRecValue += (val >> 16) & 0x7F;
+- avRecValue += (val >> 24) & 0x7F;
++ val = avRecValue / 9;
++ if (avRecValue % 9)
++ val++;
++ avRecValue = val;
++
++ if (avRecValue < 6)
++ avRecValue = 0;
++ else
++ avRecValue -= 6;
++
++ return avRecValue;
++ } else {
++ /* BIOS first programs a seed value to the phase recovery engine
++ * (recommended 19) registers.
++ * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and
++ * F2x[1,0]9C_x52.) .
++ */
++ for (index = 0x50; index <= 0x52; index ++) {
++ val = (FenceTrnFinDlySeed & 0x1F);
++ if (index != 0x52) {
++ val |= val << 8 | val << 16 | val << 24;
++ }
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+ }
+- }
+
+- val = avRecValue / 9;
+- if (avRecValue % 9)
+- val++;
+- avRecValue = val;
++ /* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
++ val |= 1 << PhyFenceTrEn;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
++
++ /* Wait 200 MEMCLKs. */
++ mct_Wait(50000); /* wait 200us */
++
++ /* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
++ val &= ~(1 << PhyFenceTrEn);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
++
++ /* BIOS reads the phase recovery engine registers
++ * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */
++ avRecValue = 0;
++ for (index = 0x50; index <= 0x52; index ++) {
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++ avRecValue += val & 0x7F;
++ if (index != 0x52) {
++ avRecValue += (val >> 8) & 0x7F;
++ avRecValue += (val >> 16) & 0x7F;
++ avRecValue += (val >> 24) & 0x7F;
++ }
++ }
+
+- /* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */
+- /* inlined mct_AdjustFenceValue() */
+- /* TODO: The RBC0 is not supported. */
+- /* if (pDCTstat->LogicalCPUID & AMD_RB_C0)
+- avRecValue -= 3;
+- else
+- */
+- if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
+- avRecValue -= 8;
+- else if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+- avRecValue -= 8;
+- else if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
+- avRecValue -= 8;
+-
+- val = Get_NB32_index_wait(dev, index_reg, 0x0C);
+- val &= ~(0x1F << 16);
+- val |= (avRecValue & 0x1F) << 16;
+- Set_NB32_index_wait(dev, index_reg, 0x0C, val);
+-
+- /* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register
+- * delays (both channels). */
+- val = Get_NB32_index_wait(dev, index_reg, 0x04);
+- Set_NB32_index_wait(dev, index_reg, 0x04, val);
++ val = avRecValue / 9;
++ if (avRecValue % 9)
++ val++;
++ avRecValue = val;
++
++ /* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */
++ /* inlined mct_AdjustFenceValue() */
++ /* TODO: The RBC0 is not supported. */
++ /* if (pDCTstat->LogicalCPUID & AMD_RB_C0)
++ avRecValue -= 3;
++ else
++ */
++ if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
++ avRecValue -= 8;
++ else if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
++ avRecValue -= 8;
++ else if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
++ avRecValue -= 8;
++
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C);
++ val &= ~(0x1F << 16);
++ val |= (avRecValue & 0x1F) << 16;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C, val);
++
++ /* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register
++ * delays (both channels).
++ */
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x04);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x04, val);
++
++ return avRecValue;
++ }
+ }
+
+ void mct_Wait(u32 cycles)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
+index f01e011..55068ce 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
+@@ -21,8 +21,14 @@
+ u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass)
+ {
+ u8 ret = 1;
+- if (pass == SecondPass)
+- ret = 0;
++
++ if (is_fam15h()) {
++ /* Fam15h needs two passes */
++ ret = 1;
++ } else {
++ if (pass == SecondPass)
++ ret = 0;
++ }
+
+ return ret;
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
+index 920f514..68acc75 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
+@@ -218,12 +218,12 @@ static void mct_setMaxRdLatTrnVal_D(struct DCTStatStruc *pDCTstat,
+ }
+
+ dev = pDCTstat->dev_dct;
+- reg = 0x78 + Channel * 0x100;
+- val = Get_NB32(dev, reg);
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, Channel, reg);
+ val &= ~(0x3ff<<22);
+ val |= MaxRdLatVal<<22;
+ /* program MaxRdLatency to correspond with current delay */
+- Set_NB32(dev, reg, val);
++ Set_NB32_DCT(dev, Channel, reg, val);
+ }
+
+ static u8 CompareMaxRdLatTestPattern_D(u32 pattern_buf, u32 addr)
+@@ -320,30 +320,28 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
+ u32 valx;
+ u32 valxx;
+ u32 index_reg;
+- u32 reg_off;
+ u32 dev;
+
+ if(pDCTstat->GangedMode)
+ Channel = 0;
+
+- index_reg = 0x98 + 0x100 * Channel;
++ index_reg = 0x98;
+
+- reg_off = 0x100 * Channel;
+ dev = pDCTstat->dev_dct;
+
+ /* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/
+- val = Get_NB32(dev, 0x88 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x88);
+ SubTotal = ((val & 0x0f) + 1) << 1; /* SubTotal is 1/2 Memclk unit */
+
+ /* If registered DIMMs are being used then add 1 MEMCLK to the sub-total*/
+- val = Get_NB32(dev, 0x90 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x90);
+ if(!(val & (1 << UnBuffDimm)))
+ SubTotal += 2;
+
+ /*If the address prelaunch is setup for 1/2 MEMCLKs then add 1,
+ * else add 2 to the sub-total. if (AddrCmdSetup || CsOdtSetup
+ * || CkeSetup) then K := K + 2; */
+- val = Get_NB32_index_wait(dev, index_reg, 0x04);
++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04);
+ if(!(val & 0x00202020))
+ SubTotal += 1;
+ else
+@@ -351,7 +349,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
+
+ /* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
+ * then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */
+- val = Get_NB32(dev, 0x78 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x78);
+ SubTotal += 8 - (val & 0x0f);
+
+ /* Convert bits 7-5 (also referred to as the course delay) of the current
+@@ -367,7 +365,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
+
+ /*New formula:
+ SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */
+- val = Get_NB32(dev, 0x94 + reg_off);
++ val = Get_NB32_DCT(dev, Channel, 0x94);
+ /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
+ val &= 7;
+ if (val >= 3) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+index 1c3e322..0ff4484 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+@@ -83,6 +83,12 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0;
+ }
+
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 1;
++ } else {
++ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 0;
++ }
++
+ pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present;
+
+ for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) {
+@@ -103,13 +109,13 @@ void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDC
+ {
+ u32 val;
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+ val |= 1 << 11;
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+ val |= 1 << 11;
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
+ }
+
+ void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
+@@ -117,15 +123,15 @@ void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
+ }
+
+ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+@@ -142,23 +148,23 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+
+ /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
+ if (DCT0Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+ val |= 1 << EnterSelfRef;
+- Set_NB32(pDCTstat->dev_dct, 0x90, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val);
+ }
+ if (DCT1Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+ val |= 1 << EnterSelfRef;
+- Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */
+ if (DCT0Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+ } while (val & (1 <<EnterSelfRef));
+ if (DCT1Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+ } while (val & (1 <<EnterSelfRef));
+ }
+
+@@ -168,8 +174,11 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+- u8 DCT0Present, DCT1Present;
+- u32 val;
++ uint8_t DCT0Present;
++ uint8_t DCT1Present;
++ uint32_t dword;
++ uint32_t mask;
++ uint32_t offset;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+@@ -177,76 +186,134 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+- /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
+- if (DCT0Present) {
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
+- val |= 1 << DisAutoComp;
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
+- }
+- if (DCT1Present) {
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
+- val |= 1 << DisAutoComp;
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
++ if (is_fam15h()) {
++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */
++ if (DCT0Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x00000190;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword);
++ }
++ if (DCT1Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x00000190;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword);
++ }
++ } else {
++ /* Program F2x[1, 0]9C[DisAutoComp]=1. */
++ if (DCT0Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8);
++ dword |= 1 << DisAutoComp;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword);
++ mct_Wait(100); /* Wait for 5us */
++ }
++ if (DCT1Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8);
++ dword |= 1 << DisAutoComp;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword);
++ mct_Wait(100); /* Wait for 5us */
++ }
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
+ if (DCT0Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
+- val &= ~(1 << MemClkFreqVal);
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
++ dword &= ~(1 << MemClkFreqVal);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
+ }
+ if (DCT1Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+- val &= ~(1 << MemClkFreqVal);
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
++ dword &= ~(1 << MemClkFreqVal);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */
++ if (is_fam15h()) {
++ offset = 0x0;
++ mask = 0x1f;
++ } else {
++ offset = 0x1;
++ mask = 0x7;
++ }
+ if (DCT0Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
+- val &= 0xFFFFFFF8;
+- val |= pDCTstat->TargetFreq - 1;
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
++ dword &= ~mask;
++ dword |= (pDCTstat->TargetFreq - offset) & mask;
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
+ }
+ if (DCT1Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+- val &= 0xFFFFFFF8;
+- val |= pDCTstat->TargetFreq - 1;
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
++ dword &= ~mask;
++ dword |= (pDCTstat->TargetFreq - offset) & mask;
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
++ }
++
++ if (is_fam15h()) {
++ if (DCT0Present) {
++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 0);
++ set_2t_configuration(pMCTstat, pDCTstat, 0);
++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 0);
++ mct_PlatformSpec(pMCTstat, pDCTstat, 0);
++ }
++ if (DCT1Present) {
++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
++ set_2t_configuration(pMCTstat, pDCTstat, 1);
++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
++ mct_PlatformSpec(pMCTstat, pDCTstat, 1);
++ }
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreqVal] = 1. */
+ if (DCT0Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
+- val |= 1 << MemClkFreqVal;
+- Set_NB32(pDCTstat->dev_dct, 0x94, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
++ dword |= 1 << MemClkFreqVal;
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
+ }
+ if (DCT1Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+- val |= 1 << MemClkFreqVal;
+- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
++ dword |= 1 << MemClkFreqVal;
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
+ }
+
+ /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
+ if (DCT0Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94);
+- } while (val & (1 << FreqChgInProg));
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
++ } while (dword & (1 << FreqChgInProg));
+ if (DCT1Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+- } while (val & (1 << FreqChgInProg));
+-
+- /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
+- if (DCT0Present) {
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
+- val &= ~(1 << DisAutoComp);
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
+- }
+- if (DCT1Present) {
+- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
+- val &= ~(1 << DisAutoComp);
+- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
++ } while (dword & (1 << FreqChgInProg));
++
++ if (is_fam15h()) {
++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */
++ if (DCT0Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x0000000f;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword);
++ }
++ if (DCT1Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006);
++ dword &= ~(0x0000ffff);
++ dword |= 0x0000000f;
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword);
++ }
++ } else {
++ /* Program F2x[1, 0]9C[DisAutoComp] = 0. */
++ if (DCT0Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8);
++ dword &= ~(1 << DisAutoComp);
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword);
++ mct_Wait(15000); /* Wait for 750us */
++ }
++ if (DCT1Present) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8);
++ dword &= ~(1 << DisAutoComp);
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword);
++ mct_Wait(15000); /* Wait for 750us */
++ }
+ }
+ }
+
+@@ -267,29 +334,46 @@ static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
+
+ /* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */
+ if (DCT0Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+ val |= 1 << ExitSelfRef;
+- Set_NB32(pDCTstat->dev_dct, 0x90, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val);
+ }
+ if (DCT1Present) {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+ val |= 1 << ExitSelfRef;
+- Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */
+ if (DCT0Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+ } while (val & (1 << ExitSelfRef));
+ if (DCT1Present)
+ do {
+- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+ } while (val & (1 << ExitSelfRef));
+ }
+
+ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
++ uint32_t dword;
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ if (is_fam15h()) {
++ /* Program F2x[1, 0]90[DisDllShutDownSR]=1. */
++ if (pDCTstat->DIMMValidDCT[0]) {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
++ dword |= (0x1 << 27);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword);
++ }
++ if (pDCTstat->DIMMValidDCT[1]) {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
++ dword |= (0x1 << 27);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword);
++ }
++ }
++
+ /* Program F2x[1,0]90[EnterSelfRefresh]=1.
+ * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
+ */
+@@ -305,11 +389,38 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ */
+ ChangeMemClk(pMCTstat, pDCTstat);
+
++ if (is_fam15h()) {
++ uint8_t dct;
++ for (dct = 0; dct < 2; dct++) {
++ if (pDCTstat->DIMMValidDCT[dct]) {
++ phyAssistedMemFnceTraining(pMCTstat, pDCTstat);
++ InitPhyCompensation(pMCTstat, pDCTstat, dct);
++ }
++ }
++ }
++
+ /* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
+ * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
+ */
+ ExitSelfRefresh(pMCTstat, pDCTstat);
+
++ if (is_fam15h()) {
++ if ((package_type == PT_C3) || (package_type == PT_GR)) {
++ /* Socket C32 or G34 */
++ /* Program F2x[1, 0]90[DisDllShutDownSR]=0. */
++ if (pDCTstat->DIMMValidDCT[0]) {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
++ dword &= ~(0x1 << 27);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword);
++ }
++ if (pDCTstat->DIMMValidDCT[1]) {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
++ dword &= ~(0x1 << 27);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword);
++ }
++ }
++ }
++
+ /* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */
+ mct_Wait(250);
+
+@@ -336,13 +447,13 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set)
+ {
+ u32 val;
+- u32 reg_off = dct * 0x100 + 0x44;
+- while (reg_off < (dct * 0x100 + 0x60)) {
+- val = Get_NB32(pDCTstat->dev_dct, reg_off);
++ u32 reg = 0x44;
++ while (reg < 0x60) {
++ val = Get_NB32_DCT(pDCTstat->dev_dct, dct, reg);
+ if (val & (1 << CSEnable))
+ set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror));
+- Set_NB32(pDCTstat->dev_dct, reg_off, val);
+- reg_off += 8;
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, reg, val);
++ reg += 8;
+ }
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 9f42d54..cbb34dc 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -30,13 +30,22 @@
+ *
+ *----------------------------------------------------------------------------
+ */
+-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue);
+-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue);
+-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl);
+-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm);
+-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass);
+-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr);
+-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
++u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
++u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
++void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++ u8 dct, u8 dimm, BOOL wl);
++void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm);
++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass);
++void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass);
++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass);
++
++static int32_t abs(int32_t val) {
++ if (val < 0)
++ val *= -1;
++
++ return val;
++}
++
+ /*
+ *-----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+@@ -62,34 +71,55 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
+ * OUT
+ *-----------------------------------------------------------------------------
+ */
+-void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+- u8 dimm, u8 pass)
++void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++ u8 dct, u8 dimm, u8 pass)
+ {
+ u8 ByteLane;
+ u32 Value, Addr;
+ u16 Addl_Data_Offset, Addl_Data_Port;
++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+
+ pDCTData->WLPass = pass;
+ /* 1. Specify the target DIMM that is to be trained by programming
+ * F2x[1, 0]9C_x08[TrDimmSel].
+ */
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
+- TrDimmSelEnd,(u32)dimm);
++ TrDimmSelEnd, (u32)dimm);
++
++ if (is_fam15h()) {
++ /* Set TrNibbleSel = 0
++ *
++ * TODO: Add support for x4 DIMMs
++ */
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
++ 2, (u32)0);
++ }
++
+ /* 2. Prepare the DIMMs for write levelization using DDR3-defined
+ * MR commands. */
+- prepareDimms(pMCTData, pDCTData,dimm, TRUE);
++ prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
++
+ /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
+ * satisfy DDR3-defined internal DRAM timing.
+ */
+- pMCTData->AgesaDelay(40);
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
++ else
++ pMCTData->AgesaDelay(40);
++
+ /* 4. Configure the processor's DDR phy for write levelization training: */
+- procConifg(pMCTData,pDCTData, dimm, pass);
++ procConfig(pMCTstat, pDCTstat, dct, dimm, pass);
++
+ /* 5. Begin write levelization training:
+- * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */
+- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
++ {
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
++ }
+ else
+ {
+ /* Broadcast write to all D3Dbyte chipset register offset 0xc
+@@ -98,7 +128,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ * retain value of 3:2 (Trdimmsel)
+ * reset bit 5 (FrzPR)
+ */
+- if (pDCTData->DctTrain)
++ if (dct)
+ {
+ Addl_Data_Offset=0x198;
+ Addl_Data_Port=0x19C;
+@@ -123,29 +153,127 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ DctAccessDone, DctAccessDone)) == 0);
+ }
+
++ if (is_fam15h())
++ proc_MFENCE();
++
+ /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
+- pMCTData->AgesaDelay(140);
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
++ else
++ pMCTData->AgesaDelay(140);
++
+ /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
++
+ /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
+ * to get the gross and fine delay settings
+ * for the target DIMM and save these values. */
+- ByteLane = 0;
+- while (ByteLane < MAX_BYTE_LANES)
+- {
+- getWLByteDelay(pDCTData,ByteLane, dimm);
+- setWLByteDelay(pDCTData,ByteLane, dimm, 1);
+- ByteLane++;
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass);
++ }
++
++ pDCTData->WLCriticalGrossDelayPrevPass = 0x1f;
++}
++
++void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++ u8 dct, u8 dimm, u8 pass)
++{
++ u8 ByteLane;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++
++ if (is_fam15h()) {
++ int32_t gross_diff[MAX_BYTE_LANES];
++ int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
++ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
++
++ /* Calculate the Critical Gross Delay */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ /* Calculate the gross delay differential for this lane */
++ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
++ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
++
++ /* WrDqDqsEarly values greater than 2 are reserved */
++ if (gross_diff[ByteLane] < -2)
++ gross_diff[ByteLane] = -2;
++
++ /* Update the Critical Gross Delay */
++ if (gross_diff[ByteLane] < cgd)
++ cgd = gross_diff[ByteLane];
++ }
++
++ pDCTData->WLCriticalGrossDelayPrevPass = cgd;
++
++ /* Compensate for occasional noise/instability causing sporadic training failure */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f);
++ uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
++ if (abs(total_delay_phy - total_delay_seed) > 0x20) {
++ printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__);
++ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
++ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
++ }
++ }
++ }
++}
++
++void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++ u8 dct, u8 dimm, u8 pass)
++{
++ u8 ByteLane;
++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++
++ if (is_fam15h()) {
++ uint32_t dword;
++ int32_t gross_diff[MAX_BYTE_LANES];
++ int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
++ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
++
++ /* Apply offset(s) if needed */
++ if (cgd < 0) {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
++ dword &= ~(0x3 << 24); /* WrDqDqsEarly = abs(cgd) */
++ dword |= ((abs(cgd) & 0x3) << 24);
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
++
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ /* Calculate the gross delay differential for this lane */
++ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
++ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
++
++ /* Prevent underflow in the presence of noise / instability*/
++ if (gross_diff[ByteLane] < cgd)
++ gross_diff[ByteLane] = cgd;
++
++ pDCTData->WLGrossDelay[index+ByteLane] = (gross_diff[ByteLane] + (abs(cgd) & 0x3));
++ }
++ } else {
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
++ dword &= ~(0x3 << 24); /* WrDqDqsEarly = 0 */
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
++ }
++ }
++
++ /* Write the adjusted gross and fine delay settings
++ * to the target DIMM. */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass);
+ }
+
+ /* 6. Configure DRAM Phy Control Register so that the phy stops driving
+ * write levelization ODT. */
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0);
+
++ if (is_fam15h())
++ proc_MFENCE();
++
+ /* Wait 10 MEMCLKs to allow for ODT signal settling. */
+- pMCTData->AgesaDelay(10);
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10);
++ else
++ pMCTData->AgesaDelay(10);
+
+ /* 7. Program the target DIMM back to normal operation by configuring
+ * the following (See section 2.8.5.4.1.1
+@@ -155,7 +283,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ * For a two DIMM system, program the Rtt value for the target DIMM
+ * to the normal operating termination:
+ */
+- prepareDimms(pMCTData, pDCTData,dimm,FALSE);
++ prepareDimms(pMCTstat, pDCTstat, dct, dimm, FALSE);
+ }
+
+ /*----------------------------------------------------------------------------
+@@ -165,7 +293,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ */
+
+ /*-----------------------------------------------------------------------------
+- * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
++ * u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
+ *
+ * Description:
+ * This function swaps the bits in MSR register value
+@@ -177,12 +305,17 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ *
+ * ----------------------------------------------------------------------------
+ */
+-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
++u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue)
+ {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+ u32 tempW, tempW1;
+
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
++ if (is_fam15h())
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15);
++ else
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10);
+ if (tempW1 & 1)
+ {
+ if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
+@@ -201,7 +334,7 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+ }
+
+ /*-----------------------------------------------------------------------------
+- * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
++ * u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
+ *
+ * Description:
+ * This function swaps the bits in MSR register value
+@@ -213,12 +346,17 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+ *
+ * ----------------------------------------------------------------------------
+ */
+-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
++u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
+ {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+ u32 tempW, tempW1;
+
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
++ if (is_fam15h())
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15);
++ else
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10);
+ if (tempW1 & 1)
+ {
+ if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
+@@ -269,7 +407,7 @@ static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms
+ return term;
+ }
+
+-static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
++static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count)
+ {
+ uint16_t term;
+
+@@ -300,27 +438,27 @@ static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms
+ *
+ * Description:
+ * This function prepares DIMMS for training
+- *
+- * Parameters:
+- * IN OUT *DCTData - Pointer to buffer with information about each DCT
+- * *SPDData - Pointer to buffer with information about each DIMMs
+- * SPD information
+- * *MCTData - Pointer to buffer with runtime parameters,
+- * IN Dimm - Logical DIMM number
+- * WL - indicates if the routine is used for Write levelization
+- * training
+- *
+- * OUT
+- *
++ * Fam10h: BKDG Rev. 3.62 section 2.8.9.9.1
++ * Fam15h: BKDG Rev. 3.14 section 2.10.5.8.1
+ * ----------------------------------------------------------------------------
+ */
+-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
++void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++ u8 dct, u8 dimm, BOOL wl)
+ {
+ u32 tempW, tempW1, tempW2, MrsBank;
+ u8 rank, currDimm, MemClkFreq;
++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
+
+- MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ if (is_fam15h()) {
++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 4);
++ } else {
++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
++ }
+ /* Configure the DCT to send initialization MR commands to the target DIMM
+ * by programming the F2x[1,0]7C register using the following steps.
+ */
+@@ -328,52 +466,95 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2))
+ {
+ /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, dimm*2+rank);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, dimm*2+rank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, dimm*2+rank);
++
+ /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
+ * register that defines the required DDR3-defined function for write
+ * levelization.
+ */
+- MrsBank = swapBankBits(pDCTData,1);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
++ MrsBank = swapBankBits(pDCTstat, dct, 1);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
++
+ /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
+ * for write levelization.
+ */
+ tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */
+
+- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+- tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
+- if (tempW2)
+- {
+- if (pDCTData->DimmX8Present[dimm])
+- tempW |= 0x800;
++ /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */
++ if (is_fam15h()) {
++ tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
++ tempW &= ~(0x0244);
++ } else {
++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
++ tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
++ if (tempW2)
++ {
++ if (pDCTData->DimmX8Present[dimm])
++ tempW |= 0x800;
++ }
+ }
+
+ /* determine Rtt_Nom for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+- tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+- } else {
++ if (is_fam15h()) {
+ if (wl) {
+- if (rank == 0) {
+- /* Get Rtt_WR for the current DIMM and rank */
+- uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+-
+- /* Convert dynamic termination code to corresponding nominal termination code */
+- if (dynamic_term == 0x200)
+- tempW1 = 0x04;
+- else if (dynamic_term == 0x400)
+- tempW1 = 0x40;
+- else
+- tempW1 = 0x0;
++ if (number_of_dimms > 1) {
++ if (rank == 0) {
++ /* Get Rtt_WR for the current DIMM and rank */
++ tempW2 = fam15_rttwr(pDCTstat, dct, dimm, rank, package_type);
++ } else {
++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
++ }
+ } else {
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
+ }
+ } else {
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
++ }
++ tempW1 = 0;
++ tempW1 |= ((tempW2 & 0x4) >> 2) << 9;
++ tempW1 |= ((tempW2 & 0x2) >> 1) << 6;
++ tempW1 |= ((tempW2 & 0x1) >> 0) << 2;
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
++ } else {
++ if (wl) {
++ if (number_of_dimms > 1) {
++ if (rank == 0) {
++ /* Get Rtt_WR for the current DIMM and rank */
++ uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]);
++
++ /* Convert dynamic termination code to corresponding nominal termination code */
++ if (dynamic_term == 0x200)
++ tempW1 = 0x04;
++ else if (dynamic_term == 0x400)
++ tempW1 = 0x40;
++ else
++ tempW1 = 0x0;
++ } else {
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ }
++ } else {
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ }
++ } else {
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ }
+ }
+ }
++
++ /* Apply Rtt_Nom to the MRS control word */
+ tempW=tempW|tempW1;
+
+ /* All ranks of the target DIMM are set to write levelization mode. */
+@@ -393,68 +574,105 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ tempW = bitTestSet(tempW1, Qoff);
+ }
+ }
+- /* Program MrsAddress[5,1]=output driver impedance control (DIC):
+- * based on F2x[1,0]84[DrvImpCtrl]
+- */
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
++
++ /* Program MrsAddress[5,1]=output driver impedance control (DIC) */
++ if (is_fam15h()) {
++ tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
++ } else {
++ /* Read DIC from F2x[1,0]84[DrvImpCtrl] */
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
++ }
++
++ /* Apply DIC to the MRS control word */
+ if (bitTest(tempW1, 1))
+ tempW = bitTestSet(tempW, 5);
+ if (bitTest(tempW1, 0))
+ tempW = bitTestSet(tempW, 1);
+
+- tempW = swapAddrBits_wl(pDCTData, tempW);
++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
++
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
+
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
+ * the specified DIMM.
+ */
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
+ /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
+- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
+ {
+ }
++
+ /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
+ * register that defines the required DDR3-defined function for Rtt_WR.
+ */
+- MrsBank = swapBankBits(pDCTData,2);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
++ MrsBank = swapBankBits(pDCTstat, dct, 2);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
++
+ /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
+ * for Rtt_WR (DRAMTermDyn).
+ */
+ tempW = 0;/* PASR = 0,*/
+- /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
+- * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
+- if (bitTest(tempW1,19))
+- {tempW = bitTestSet(tempW, 7);}
+- if (bitTest(tempW1,18))
+- {tempW = bitTestSet(tempW, 6);}
+- /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
+- tempW=tempW|((tempW1&0x00700000)>>17);
+- /* workaround for DR-B0 */
+- if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+- tempW+=0x8;
++
++ /* Retrieve normal settings of the MRS control word and clear Rtt_WR */
++ if (is_fam15h()) {
++ tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
++ tempW &= ~(0x0600);
++ } else {
++ /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
++ * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
++ if (bitTest(tempW1,19))
++ {tempW = bitTestSet(tempW, 7);}
++ if (bitTest(tempW1,18))
++ {tempW = bitTestSet(tempW, 6);}
++ /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
++ tempW=tempW|((tempW1&0x00700000)>>17);
++ /* workaround for DR-B0 */
++ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
++ tempW+=0x8;
++ }
++
+ /* determine Rtt_WR for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+- else
+- tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
++ if (is_fam15h()) {
++ tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
++ tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
++ else
++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]);
++ }
++
++ /* Apply Rtt_WR to the MRS control word */
+ tempW=tempW|tempW1;
+- tempW = swapAddrBits_wl(pDCTData,tempW);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
++
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
+ the specified DIMM.*/
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
++
+ /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
+- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
+ {
+ }
+@@ -473,97 +691,163 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ rank = 0;
+ while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2))
+ {
+-
+ /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
+ * to be trained.
+ */
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, currDimm*2+rank);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, currDimm*2+rank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, currDimm*2+rank);
++
+ /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal
+ * DRAM register that defines the required DDR3-defined function
+ * for write levelization.
+ */
+- MrsBank = swapBankBits(pDCTData,1);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
++ MrsBank = swapBankBits(pDCTstat, dct, 1);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
++
+ /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required
+ * DDR3-defined function for write levelization.
+ */
+ tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */
+
+- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+- tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
+- if (tempW2)
+- {
+- if (pDCTData->DimmX8Present[currDimm])
+- tempW |= 0x800;
++ /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */
++ if (is_fam15h()) {
++ tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
++ tempW &= ~(0x0244);
++ } else {
++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
++ tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
++ if (tempW2)
++ {
++ if (pDCTData->DimmX8Present[currDimm])
++ tempW |= 0x800;
++ }
+ }
+
+ /* determine Rtt_Nom for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+- else
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ if (is_fam15h()) {
++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
++ tempW1 = 0;
++ tempW1 |= ((tempW2 & 0x4) >> 2) << 9;
++ tempW1 |= ((tempW2 & 0x2) >> 1) << 6;
++ tempW1 |= ((tempW2 & 0x1) >> 0) << 2;
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
++ tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
++ else
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ }
++
++ /* Apply Rtt_Nom to the MRS control word */
+ tempW=tempW|tempW1;
+- /* program MrsAddress[5,1]=output driver impedance control (DIC):
+- * based on F2x[1,0]84[DrvImpCtrl] */
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
++
++ /* Program MrsAddress[5,1]=output driver impedance control (DIC) */
++ if (is_fam15h()) {
++ tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
++ } else {
++ /* Read DIC from F2x[1,0]84[DrvImpCtrl] */
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
++ }
++
++ /* Apply DIC to the MRS control word */
+ if (bitTest(tempW1,1))
+ {tempW = bitTestSet(tempW, 5);}
+ if (bitTest(tempW1,0))
+ {tempW = bitTestSet(tempW, 1);}
+- tempW = swapAddrBits_wl(pDCTData,tempW);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
++
++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
++
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
++
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command
+ * to the specified DIMM.
+ */
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ set_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
++
+ /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
+- while ((get_Bits(pDCTData, pDCTData->CurrDct,
++ while ((get_Bits(pDCTData, dct,
+ pDCTData->NodeId, FUN_DCT, DRAM_INIT,
+ SendMrsCmd, SendMrsCmd)) == 1);
++
+ /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
+ * register that defines the required DDR3-defined function for Rtt_WR.
+ */
+- MrsBank = swapBankBits(pDCTData,2);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
++ MrsBank = swapBankBits(pDCTstat, dct, 2);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
++
+ /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
+ * for Rtt_WR (DRAMTermDyn).
+ */
+ tempW = 0;/* PASR = 0,*/
+- /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
+- * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
+- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
+- if (bitTest(tempW1,19))
+- {tempW = bitTestSet(tempW, 7);}
+- if (bitTest(tempW1,18))
+- {tempW = bitTestSet(tempW, 6);}
+- /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
+- tempW=tempW|((tempW1&0x00700000)>>17);
+- /* workaround for DR-B0 */
+- if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+- tempW+=0x8;
++
++ /* Retrieve normal settings of the MRS control word and clear Rtt_WR */
++ if (is_fam15h()) {
++ tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
++ tempW &= ~(0x0600);
++ } else {
++ /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
++ * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
++ if (bitTest(tempW1,19))
++ {tempW = bitTestSet(tempW, 7);}
++ if (bitTest(tempW1,18))
++ {tempW = bitTestSet(tempW, 6);}
++ /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
++ tempW=tempW|((tempW1&0x00700000)>>17);
++ /* workaround for DR-B0 */
++ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
++ tempW+=0x8;
++ }
++
+ /* determine Rtt_WR for WL & Normal mode */
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+- else
+- tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ if (is_fam15h()) {
++ tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
++ tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
++ else
++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]);
++ }
++
++ /* Apply Rtt_WR to the MRS control word */
+ tempW=tempW|tempW1;
+- tempW = swapAddrBits_wl(pDCTData,tempW);
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
++ if (is_fam15h())
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
++ else
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
++
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
+ the specified DIMM.*/
+- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
++
+ /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
+- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
+ {
+ }
+@@ -587,29 +871,60 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
++void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm)
+ {
++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++
+ u8 WrLvOdt1=0;
+
+- if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
+- if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
+- WrLvOdt1 = 0x03;
+- } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
+- WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
++ if (is_fam15h()) {
++ /* Convert DIMM number to CS */
++ uint32_t dword;
++ uint8_t cs;
++ uint8_t rank = 0;
++
++ cs = (dimm * 2) + rank;
++
++ /* Fetch preprogammed ODT pattern from configuration registers */
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, ((cs>3)?0x23c:0x238));
++ if ((cs == 7) || (cs == 3))
++ WrLvOdt1 = ((dword >> 24) & 0xf);
++ else if ((cs == 6) || (cs == 2))
++ WrLvOdt1 = ((dword >> 16) & 0xf);
++ else if ((cs == 5) || (cs == 1))
++ WrLvOdt1 = ((dword >> 8) & 0xf);
++ else if ((cs == 4) || (cs == 0))
++ WrLvOdt1 = (dword & 0xf);
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
++ if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
++ WrLvOdt1 = 0x03;
++ } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
++ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
++ } else {
++ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
++ }
+ } else {
+- WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
++ WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
+ }
+- } else {
+- WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
+ }
+
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
+
+ }
+
++#ifdef UNUSED_CODE
++static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
++{
++ uint16_t fam15h_next_lowest_freq_tab[] = {0, 0, 0, 0, 0x4, 0, 0x4, 0, 0, 0, 0x6, 0, 0, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12};
++ return fam15h_next_lowest_freq_tab[memclk_freq];
++}
++#endif
++
+ /*-----------------------------------------------------------------------------
+- * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
++ * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
+ *
+ * Description:
+ * This function programs the ODT values for the NB
+@@ -622,31 +937,43 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass)
+ {
+- u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq;
++ u8 ByteLane, MemClkFreq;
++ int32_t Seed_Gross;
++ int32_t Seed_Fine;
++ uint8_t Seed_PreGross;
+ u32 Value, Addr;
+ u16 Addl_Data_Offset, Addl_Data_Port;
+- u16 freq_tab[] = {400, 533, 667, 800};
++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
++ u16 fam10h_freq_tab[] = {400, 533, 667, 800};
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
+- /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
+- MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+- FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
++ if (is_fam15h()) {
++ /* MemClkFreq: 0x4: 333MHz; 0x6: 400MHz; 0xa: 533MHz; 0xe: 667MHz; 0x12: 800MHz; 0x16: 933MHz */
++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 4);
++ } else {
++ /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
++ }
+
+ /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
+ * current memory subsystem configuration.
+ */
+- programODT(pMCTData, pDCTData, dimm);
++ programODT(pMCTstat, pDCTstat, dct, dimm);
+
+ /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
+- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) {
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) {
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, (u32)1);
+ }
+ else
+ {
+ /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */
+- if (pDCTData->DctTrain)
++ if (dct)
+ {
+ Addl_Data_Offset=0x198;
+ Addl_Data_Port=0x19C;
+@@ -669,33 +996,94 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ DctAccessDone, DctAccessDone)) == 0);
+ }
+
++ if (is_fam15h())
++ proc_MFENCE();
++
+ /* Wait 10 MEMCLKs to allow for ODT signal settling. */
+- pMCTData->AgesaDelay(10);
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10);
++ else
++ pMCTData->AgesaDelay(10);
++
++ /* Program write levelling seed values */
+ if (pass == 1)
+ {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- {
+- if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
++ /* Pass 1 */
++ if (is_fam15h()) {
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t Seed_Total = 0;
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 96 */
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ Seed_Total = 0x41;
++ } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
++ Seed_Total = 0x0;
++ } else {
++ Seed_Total = 0xf;
++ }
++ } else if (package_type == PT_C3) {
++ /* Socket C32: Fam15h BKDG v3.14 Table 97 */
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ Seed_Total = 0x3e;
++ } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
++ Seed_Total = 0x0;
++ } else {
++ Seed_Total = 0x12;
++ }
++ } else if (package_type == PT_M2) {
++ /* Socket AM3: Fam15h BKDG v3.14 Table 98 */
++ Seed_Total = 0xf;
++ }
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
++ Seed_Total += ((AddrCmdPrelaunch)?0x10:0x0);
++
++ /* Adjust seed for the minimum platform supported frequency */
++ Seed_Total = (int32_t) (((((int64_t) Seed_Total) *
++ fam15h_freq_tab[MemClkFreq] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++
++ Seed_Gross = (Seed_Total >> 5) & 0x1f;
++ Seed_Fine = Seed_Total & 0x1f;
++
++ /* Save seed values for later use */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++
++ if (Seed_Gross == 0)
++ Seed_PreGross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_PreGross = 1;
++ else
++ Seed_PreGross = 2;
++
++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++ }
++ } else {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ {
+- Seed_Gross = 0x02;
+- Seed_Fine = 0x16;
++ if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct))))
++ {
++ Seed_Gross = 0x02;
++ Seed_Fine = 0x16;
++ }
++ else
++ {
++ Seed_Gross = 0x02;
++ Seed_Fine = 0x00;
++ }
+ }
+ else
+ {
+- Seed_Gross = 0x02;
+- Seed_Fine = 0x00;
+- }
+- }
+- else
+- {
+- if (MemClkFreq == 6) {
+- /* DDR-800 */
+- Seed_Gross = 0x00;
+- Seed_Fine = 0x1a;
+- } else {
+- /* Use settings for DDR-400 (interpolated from BKDG) */
+- Seed_Gross = 0x00;
+- Seed_Fine = 0x0d;
++ if (MemClkFreq == 6) {
++ /* DDR-800 */
++ Seed_Gross = 0x00;
++ Seed_Fine = 0x1a;
++ } else {
++ /* Use settings for DDR-400 (interpolated from BKDG) */
++ Seed_Gross = 0x00;
++ Seed_Fine = 0x0d;
++ }
+ }
+ }
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+@@ -711,39 +1099,91 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+ }
+- } else { /* Pass 2 */
++ } else {
++ /* Pass 2 */
+ /* From BKDG, Write Leveling Seed Value. */
+- u32 RegisterDelay, SeedTotal;
+- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+- {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
+- else
+- RegisterDelay = 0;
+- SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+- (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
+- /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
+- training) - RegisterDelay. */
+- SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
+- freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 100)));
+- Seed_Gross = SeedTotal / 32;
+- Seed_Fine = SeedTotal & 0x1f;
+- if (Seed_Gross == 0)
+- Seed_Gross = 0;
+- else if (Seed_Gross & 0x1)
+- Seed_Gross = 1;
+- else
+- Seed_Gross = 2;
+- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ if (is_fam15h()) {
++ uint32_t RegisterDelay;
++ int32_t SeedTotal;
++ int32_t SeedTotalPreScaling;
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (AddrCmdPrelaunch)
++ RegisterDelay = 0x30;
++ else
++ RegisterDelay = 0x20;
++ } else {
++ RegisterDelay = 0;
++ }
++ /* Retrieve WrDqDqsEarly */
++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value);
++
++ /* Calculate adjusted seed values */
++ SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
++ SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value));
++ SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) *
++ fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
++
++ if (SeedTotal >= 0) {
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal % 32;
++ } else {
++ Seed_Gross = (SeedTotal / 32) - 1;
++ Seed_Fine = (SeedTotal % 32) + 32;
++ }
++
++ if (Seed_Gross == 0)
++ Seed_PreGross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_PreGross = 1;
++ else
++ Seed_PreGross = 2;
++
++ /* Save seed values for later use */
++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ }
++ } else {
++ u32 RegisterDelay, SeedTotal;
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
++ {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED])
++ RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
++ else
++ RegisterDelay = 0;
++ SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
++ /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
++ training) - RegisterDelay. */
++ SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
++ fam10h_freq_tab[MemClkFreq-3] * 100) / (fam10h_freq_tab[0] * 100)));
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal & 0x1f;
++ if (Seed_Gross == 0)
++ Seed_Gross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_Gross = 1;
++ else
++ Seed_Gross = 2;
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ }
+ }
+ }
+
+- setWLByteDelay(pDCTData, ByteLane, dimm, 0);
++ pDCTData->WLPrevMemclkFreq = MemClkFreq;
++ setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass);
+ }
+
+ /*-----------------------------------------------------------------------------
+- * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
++ * void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm){
+ *
+ * Description:
+ * This function writes the write levelization byte delay for the Phase
+@@ -763,8 +1203,9 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+ *
+ *-----------------------------------------------------------------------------
+ */
+-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
++void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass)
+ {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+ u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
+ u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW;
+
+@@ -777,22 +1218,26 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+ EccValue = 0;
+ while (ByteLane < MAX_BYTE_LANES)
+ {
+- /* This subtract 0xC workaround might be temporary. */
+- if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain))))
+- {
+- tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
+- tempW -= 0xC;
+- pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
+- pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
+- }
+- grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
+- /* Adjust seed gross delay overflow (greater than 3):
+- * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
+- * - Keep original seed gross delay for later reference.
+- */
+- if(grossDelayValue >= 3)
+- {
+- grossDelayValue = (grossDelayValue&1)? 1 : 2;
++ if (is_fam15h()) {
++ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
++ } else {
++ /* This subtract 0xC workaround might be temporary. */
++ if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+dct))))
++ {
++ tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
++ tempW -= 0xC;
++ pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
++ pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
++ }
++ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
++ /* Adjust seed gross delay overflow (greater than 3):
++ * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
++ * - Keep original seed gross delay for later reference.
++ */
++ if(grossDelayValue >= 3)
++ {
++ grossDelayValue = (grossDelayValue&1)? 1 : 2;
++ }
+ }
+ fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
+ if (ByteLane < 4)
+@@ -803,15 +1248,16 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+ EccValue = ((grossDelayValue << 5) | fineDelayValue);
+ ByteLane++;
+ }
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_CONT_ADD_PHASE_REC_CTRL_LOW, 0, 31, (u32)ValueLow);
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh);
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue);
+ }
+ else
+ {
++ /* Fam10h BKDG Rev. 3.62 2.8.9.9.1 (6) */
+ index = (u8)(MAX_BYTE_LANES * dimm);
+ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
+ fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
+@@ -841,16 +1287,24 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+ grossStartLoc = (u8)(fineEndLoc + 1);
+ grossEndLoc = (u8)(grossStartLoc + 1);
+
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue);
+- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue);
++
++ pDCTData->WLFineDelayPrevPass[index+ByteLane] = fineDelayValue;
++ pDCTData->WLGrossDelayPrevPass[index+ByteLane] = grossDelayValue;
++ if (pass == FirstPass) {
++ pDCTData->WLFineDelayFirstPass[index+ByteLane] = fineDelayValue;
++ pDCTData->WLGrossDelayFirstPass[index+ByteLane] = grossDelayValue;
++ pDCTData->WLCriticalGrossDelayFirstPass = pDCTData->WLCriticalGrossDelayPrevPass;
++ }
+ }
+
+ }
+
+ /*-----------------------------------------------------------------------------
+- * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
++ * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm)
+ *
+ * Description:
+ * This function reads the write levelization byte delay from the Phase
+@@ -868,8 +1322,9 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+ *
+ *-----------------------------------------------------------------------------
+ */
+-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass)
+ {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+ u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
+ u32 addr, fine, gross;
+ tempB = 0;
+@@ -890,25 +1345,31 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
+ grossStartLoc = (u8)(fineEndLoc + 1);
+ grossEndLoc = (u8)(grossStartLoc + 1);
+
+- fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
++ fine = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc);
+- gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
++ gross = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc);
+- /* Adjust seed gross delay overflow (greater than 3):
+- * - Adjust the trained gross delay to the original seed gross delay.
+- */
+- if (pDCTData->WLGrossDelay[index+ByteLane] >= 3) {
+- gross += pDCTData->WLGrossDelay[index+ByteLane];
+- if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
+- gross -= 1;
+- else
+- gross -= 2;
+- } else if ((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) {
+- /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
+- * We will then round the negative number to 0.
++
++ if (!is_fam15h()) {
++ /* Adjust seed gross delay overflow (greater than 3):
++ * - Adjust the trained gross delay to the original seed gross delay.
+ */
+- gross = 0;
+- fine = 0;
++ if(pDCTData->WLGrossDelay[index+ByteLane] >= 3)
++ {
++ gross += pDCTData->WLGrossDelay[index+ByteLane];
++ if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
++ gross -= 1;
++ else
++ gross -= 2;
++ }
++ else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3))
++ {
++ /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
++ * We will then round the negative number to 0.
++ */
++ gross = 0;
++ fine = 0;
++ }
+ }
+ pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
+ pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
+index 0466c77..cf6afaa 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -134,24 +135,48 @@ static u32 get_Bits(sDCTStruct *pDCTData,
+ u16 offset, u8 low, u8 high)
+ {
+ u32 temp;
++ uint32_t dword;
++
+ /* ASSERT(node < MAX_NODES); */
+ if (dct == BOTH_DCTS)
+ {
+ /* Registers exist on DCT0 only */
++ if (is_fam15h())
++ {
++ /* Select DCT 0 */
++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ dword &= ~0x1;
++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ }
++
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+- if (dct == 1)
++ if (is_fam15h())
+ {
+- /* Write to dct 1 */
+- offset += 0x100;
++ /* Select DCT */
++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++
++ /* Read from the selected DCT */
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+- /* Write to dct 0 */
+- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ if (dct == 1)
++ {
++ /* Read from dct 1 */
++ offset += 0x100;
++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ }
++ else
++ {
++ /* Read from dct 0 */
++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ }
+ }
+ }
+ return temp;
+@@ -184,25 +209,49 @@ static void set_Bits(sDCTStruct *pDCTData,
+ u16 offset, u8 low, u8 high, u32 value)
+ {
+ u32 temp;
++ uint32_t dword;
++
+ temp = value;
+
+ if (dct == BOTH_DCTS)
+ {
+ /* Registers exist on DCT0 only */
++ if (is_fam15h())
++ {
++ /* Select DCT 0 */
++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ dword &= ~0x1;
++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ }
++
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+- if (dct == 1)
++ if (is_fam15h())
+ {
+- /* Write to dct 1 */
+- offset += 0x100;
++ /* Select DCT */
++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
++
++ /* Write to the selected DCT */
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+- /* Write to dct 0 */
+- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ if (dct == 1)
++ {
++ /* Write to dct 1 */
++ offset += 0x100;
++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ }
++ else
++ {
++ /* Write to dct 0 */
++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
++ }
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+index f846d87..162340e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -33,7 +34,8 @@
+ #define C_MAX_DIMMS 4 /* Maximum Number of DIMMs on each DCT */
+
+ /* STATUS Definition */
+-#define DCT_STATUS_REGISTERED 3 /* Registered DIMMs support */
++#define DCT_STATUS_REGISTERED 3 /* Registered DIMMs support */
++#define DCT_STATUS_LOAD_REDUCED 4 /* Load-Reduced DIMMs support */
+ #define DCT_STATUS_OnDimmMirror 24 /* OnDimmMirror support */
+
+ /* PCI Defintions */
+@@ -78,12 +80,18 @@
+ #define SendMrsCmd 26
+ #define Qoff 12
+ #define MRS_Level 7
+-#define MrsAddressStart 0
+-#define MrsAddressEnd 15
+-#define MrsBankStart 16
+-#define MrsBankEnd 18
+-#define MrsChipSelStart 20
+-#define MrsChipSelEnd 22
++#define MrsAddressStartFam10 0
++#define MrsAddressEndFam10 15
++#define MrsAddressStartFam15 0
++#define MrsAddressEndFam15 17
++#define MrsBankStartFam10 16
++#define MrsBankEndFam10 18
++#define MrsBankStartFam15 18
++#define MrsBankEndFam15 20
++#define MrsChipSelStartFam10 20
++#define MrsChipSelEndFam10 22
++#define MrsChipSelStartFam15 21
++#define MrsChipSelEndFam15 23
+ #define ASR 18
+ #define SRT 19
+ #define DramTermDynStart 10
+@@ -115,10 +123,32 @@ typedef struct _sDCTStruct
+ u8 DctTrain; /* Current DCT being trained */
+ u8 CurrDct; /* Current DCT number (0 or 1) */
+ u8 DctCSPresent; /* Current DCT CS mapping */
++ int32_t WLSeedGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
++ int32_t WLSeedFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Fine Delay */
++ /* per byte Lane Per Logical DIMM*/
++ int32_t WLSeedPreGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Pre-Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
+ u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */
+ /* per byte Lane Per Logical DIMM*/
+ u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */
+ /* per byte Lane Per Logical DIMM*/
++ u8 WLGrossDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLFineDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Fine Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLGrossDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Previous Pass Write Levelization Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLFineDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Previous Pass Write Levelization Fine Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLGrossDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Final-Pass Write Levelization Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLFineDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Final-Pass Write Levelization Fine Delay */
++ /* per byte Lane Per Logical DIMM*/
++ int32_t WLCriticalGrossDelayFirstPass;
++ int32_t WLCriticalGrossDelayPrevPass;
++ int32_t WLCriticalGrossDelayFinalPass;
++ uint16_t WLPrevMemclkFreq;
+ u16 RegMan1Present;
+ u8 DimmPresent[MAX_TOTAL_DIMMS];/* Indicates which DIMMs are present */
+ /* from Total Number of DIMMs(per Node)*/
+@@ -132,7 +162,7 @@ typedef struct _sDCTStruct
+ /* per byte lane */
+ u8 MaxDimmsInstalled; /* Max Dimms Installed for current DCT */
+ u8 DimmRanks[MAX_TOTAL_DIMMS]; /* Total Number of Ranks(per Dimm) */
+- u32 LogicalCPUID;
++ uint64_t LogicalCPUID;
+ u8 WLPass;
+ } sDCTStruct;
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index c9bcac1..aa23951 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -18,6 +18,7 @@
+ */
+
+ #include <string.h>
++#include <arch/cpu.h>
+ #include <arch/acpi.h>
+ #include <cpu/x86/msr.h>
+ #include <device/device.h>
+@@ -32,6 +33,23 @@
+
+ #define S3NV_FILE_NAME "s3nv"
+
++#ifdef __RAMSTAGE__
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++#endif
++
+ static ssize_t get_s3nv_file_offset(void);
+
+ ssize_t get_s3nv_file_offset(void)
+@@ -47,6 +65,28 @@ ssize_t get_s3nv_file_offset(void)
+ return s3nv_region.region.offset;
+ }
+
++static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg) {
++ if (is_fam15h()) {
++ uint32_t dword;
++#ifdef __PRE_RAM__
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++#else
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++#endif
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ reg += dct * 0x100;
++ }
++
++ return pci_read_config32(dev, reg);
++}
++
+ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
+ {
+ uint32_t dword;
+@@ -61,12 +101,54 @@ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg
+ return dword;
+ }
+
++static 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)
++{
++ if (is_fam15h()) {
++ uint32_t dword;
++#ifdef __PRE_RAM__
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++#else
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++#endif
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ index_ctl_reg += dct * 0x100;
++ }
++
++ return read_amd_dct_index_register(dev, index_ctl_reg, index);
++}
++
+ #ifdef __RAMSTAGE__
+ static uint64_t rdmsr_uint64_t(unsigned long index) {
+ msr_t msr = rdmsr(index);
+ return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo);
+ }
+
++static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg) {
++ uint32_t dword;
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++
++ /* Select NB Pstate index */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~(0x3 << 4);
++ dword |= (nb_pstate & 0x3) << 4;
++ pci_write_config32(dev_fn1, 0x10c, dword);
++
++ return pci_read_config32(dev, reg);
++}
++
+ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
+ {
+ uint8_t i;
+@@ -82,7 +164,8 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
+ device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2));
+ device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3));
+- if ((!dev_fn1) || (!dev_fn2) || (!dev_fn3)) {
++ /* Test for node presence */
++ if ((!dev_fn1) || (pci_read_config32(dev_fn1, PCI_VENDOR_ID) == 0xffffffff)) {
+ persistent_data->node[node].node_present = 0;
+ continue;
+ }
+@@ -95,22 +178,22 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+ data->f2x110 = pci_read_config32(dev_fn2, 0x110);
+
+ /* Stage 2 */
+- data->f1x40 = pci_read_config32(dev_fn1, 0x40 + (0x100 * channel));
+- data->f1x44 = pci_read_config32(dev_fn1, 0x44 + (0x100 * channel));
+- data->f1x48 = pci_read_config32(dev_fn1, 0x48 + (0x100 * channel));
+- data->f1x4c = pci_read_config32(dev_fn1, 0x4c + (0x100 * channel));
+- data->f1x50 = pci_read_config32(dev_fn1, 0x50 + (0x100 * channel));
+- data->f1x54 = pci_read_config32(dev_fn1, 0x54 + (0x100 * channel));
+- data->f1x58 = pci_read_config32(dev_fn1, 0x58 + (0x100 * channel));
+- data->f1x5c = pci_read_config32(dev_fn1, 0x5c + (0x100 * channel));
+- data->f1x60 = pci_read_config32(dev_fn1, 0x60 + (0x100 * channel));
+- data->f1x64 = pci_read_config32(dev_fn1, 0x64 + (0x100 * channel));
+- data->f1x68 = pci_read_config32(dev_fn1, 0x68 + (0x100 * channel));
+- data->f1x6c = pci_read_config32(dev_fn1, 0x6c + (0x100 * channel));
+- data->f1x70 = pci_read_config32(dev_fn1, 0x70 + (0x100 * channel));
+- data->f1x74 = pci_read_config32(dev_fn1, 0x74 + (0x100 * channel));
+- data->f1x78 = pci_read_config32(dev_fn1, 0x78 + (0x100 * channel));
+- data->f1x7c = pci_read_config32(dev_fn1, 0x7c + (0x100 * channel));
++ data->f1x40 = read_config32_dct(dev_fn1, node, channel, 0x40);
++ data->f1x44 = read_config32_dct(dev_fn1, node, channel, 0x44);
++ data->f1x48 = read_config32_dct(dev_fn1, node, channel, 0x48);
++ data->f1x4c = read_config32_dct(dev_fn1, node, channel, 0x4c);
++ data->f1x50 = read_config32_dct(dev_fn1, node, channel, 0x50);
++ data->f1x54 = read_config32_dct(dev_fn1, node, channel, 0x54);
++ data->f1x58 = read_config32_dct(dev_fn1, node, channel, 0x58);
++ data->f1x5c = read_config32_dct(dev_fn1, node, channel, 0x5c);
++ data->f1x60 = read_config32_dct(dev_fn1, node, channel, 0x60);
++ data->f1x64 = read_config32_dct(dev_fn1, node, channel, 0x64);
++ data->f1x68 = read_config32_dct(dev_fn1, node, channel, 0x68);
++ data->f1x6c = read_config32_dct(dev_fn1, node, channel, 0x6c);
++ data->f1x70 = read_config32_dct(dev_fn1, node, channel, 0x70);
++ data->f1x74 = read_config32_dct(dev_fn1, node, channel, 0x74);
++ data->f1x78 = read_config32_dct(dev_fn1, node, channel, 0x78);
++ data->f1x7c = read_config32_dct(dev_fn1, node, channel, 0x7c);
+ data->f1xf0 = pci_read_config32(dev_fn1, 0xf0);
+ data->f1x120 = pci_read_config32(dev_fn1, 0x120);
+ data->f1x124 = pci_read_config32(dev_fn1, 0x124);
+@@ -134,75 +217,144 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+ data->msrc001001f = rdmsr_uint64_t(0xc001001f);
+
+ /* Stage 3 */
+- data->f2x40 = pci_read_config32(dev_fn2, 0x40 + (0x100 * channel));
+- data->f2x44 = pci_read_config32(dev_fn2, 0x44 + (0x100 * channel));
+- data->f2x48 = pci_read_config32(dev_fn2, 0x48 + (0x100 * channel));
+- data->f2x4c = pci_read_config32(dev_fn2, 0x4c + (0x100 * channel));
+- data->f2x50 = pci_read_config32(dev_fn2, 0x50 + (0x100 * channel));
+- data->f2x54 = pci_read_config32(dev_fn2, 0x54 + (0x100 * channel));
+- data->f2x58 = pci_read_config32(dev_fn2, 0x58 + (0x100 * channel));
+- data->f2x5c = pci_read_config32(dev_fn2, 0x5c + (0x100 * channel));
+- data->f2x60 = pci_read_config32(dev_fn2, 0x60 + (0x100 * channel));
+- data->f2x64 = pci_read_config32(dev_fn2, 0x64 + (0x100 * channel));
+- data->f2x68 = pci_read_config32(dev_fn2, 0x68 + (0x100 * channel));
+- data->f2x6c = pci_read_config32(dev_fn2, 0x6c + (0x100 * channel));
+- data->f2x78 = pci_read_config32(dev_fn2, 0x78 + (0x100 * channel));
+- data->f2x7c = pci_read_config32(dev_fn2, 0x7c + (0x100 * channel));
+- data->f2x80 = pci_read_config32(dev_fn2, 0x80 + (0x100 * channel));
+- data->f2x84 = pci_read_config32(dev_fn2, 0x84 + (0x100 * channel));
+- data->f2x88 = pci_read_config32(dev_fn2, 0x88 + (0x100 * channel));
+- data->f2x8c = pci_read_config32(dev_fn2, 0x8c + (0x100 * channel));
+- data->f2x90 = pci_read_config32(dev_fn2, 0x90 + (0x100 * channel));
+- data->f2xa4 = pci_read_config32(dev_fn2, 0xa4 + (0x100 * channel));
+- data->f2xa8 = pci_read_config32(dev_fn2, 0xa8 + (0x100 * channel));
++ data->f2x40 = read_config32_dct(dev_fn2, node, channel, 0x40);
++ data->f2x44 = read_config32_dct(dev_fn2, node, channel, 0x44);
++ data->f2x48 = read_config32_dct(dev_fn2, node, channel, 0x48);
++ data->f2x4c = read_config32_dct(dev_fn2, node, channel, 0x4c);
++ data->f2x50 = read_config32_dct(dev_fn2, node, channel, 0x50);
++ data->f2x54 = read_config32_dct(dev_fn2, node, channel, 0x54);
++ data->f2x58 = read_config32_dct(dev_fn2, node, channel, 0x58);
++ data->f2x5c = read_config32_dct(dev_fn2, node, channel, 0x5c);
++ data->f2x60 = read_config32_dct(dev_fn2, node, channel, 0x60);
++ data->f2x64 = read_config32_dct(dev_fn2, node, channel, 0x64);
++ data->f2x68 = read_config32_dct(dev_fn2, node, channel, 0x68);
++ data->f2x6c = read_config32_dct(dev_fn2, node, channel, 0x6c);
++ data->f2x78 = read_config32_dct(dev_fn2, node, channel, 0x78);
++ data->f2x7c = read_config32_dct(dev_fn2, node, channel, 0x7c);
++ data->f2x80 = read_config32_dct(dev_fn2, node, channel, 0x80);
++ data->f2x84 = read_config32_dct(dev_fn2, node, channel, 0x84);
++ data->f2x88 = read_config32_dct(dev_fn2, node, channel, 0x88);
++ data->f2x8c = read_config32_dct(dev_fn2, node, channel, 0x8c);
++ data->f2x90 = read_config32_dct(dev_fn2, node, channel, 0x90);
++ data->f2xa4 = read_config32_dct(dev_fn2, node, channel, 0xa4);
++ data->f2xa8 = read_config32_dct(dev_fn2, node, channel, 0xa8);
++
++ /* Family 15h-specific configuration */
++ if (is_fam15h()) {
++ data->f2x200 = read_config32_dct(dev_fn2, node, channel, 0x200);
++ data->f2x204 = read_config32_dct(dev_fn2, node, channel, 0x204);
++ data->f2x208 = read_config32_dct(dev_fn2, node, channel, 0x208);
++ data->f2x20c = read_config32_dct(dev_fn2, node, channel, 0x20c);
++ for (i=0; i<4; i++)
++ data->f2x210[i] = read_config32_dct_nbpstate(dev_fn2, node, channel, i, 0x210);
++ data->f2x214 = read_config32_dct(dev_fn2, node, channel, 0x214);
++ data->f2x218 = read_config32_dct(dev_fn2, node, channel, 0x218);
++ data->f2x21c = read_config32_dct(dev_fn2, node, channel, 0x21c);
++ data->f2x22c = read_config32_dct(dev_fn2, node, channel, 0x22c);
++ data->f2x230 = read_config32_dct(dev_fn2, node, channel, 0x230);
++ data->f2x234 = read_config32_dct(dev_fn2, node, channel, 0x234);
++ data->f2x238 = read_config32_dct(dev_fn2, node, channel, 0x238);
++ data->f2x23c = read_config32_dct(dev_fn2, node, channel, 0x23c);
++ data->f2x240 = read_config32_dct(dev_fn2, node, channel, 0x240);
++
++ data->f2x9cx0d0fe003 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe003);
++ data->f2x9cx0d0fe013 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe013);
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_8_0_1f[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f001f | (i << 8));
++ data->f2x9cx0d0f201f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f201f);
++ data->f2x9cx0d0f211f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f211f);
++ data->f2x9cx0d0f221f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f221f);
++ data->f2x9cx0d0f801f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f801f);
++ data->f2x9cx0d0f811f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f811f);
++ data->f2x9cx0d0f821f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f821f);
++ data->f2x9cx0d0fc01f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc01f);
++ data->f2x9cx0d0fc11f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc11f);
++ data->f2x9cx0d0fc21f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc21f);
++ data->f2x9cx0d0f4009 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f4009);
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_8_0_02[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0002 | (i << 8));
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_8_0_06[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0006 | (i << 8));
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_8_0_0a[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f000a | (i << 8));
++
++ data->f2x9cx0d0f2002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2002);
++ data->f2x9cx0d0f2102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2102);
++ data->f2x9cx0d0f2202 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2202);
++ data->f2x9cx0d0f8002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8002);
++ data->f2x9cx0d0f8006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8006);
++ data->f2x9cx0d0f800a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f800a);
++ data->f2x9cx0d0f8102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8102);
++ data->f2x9cx0d0f8106 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8106);
++ data->f2x9cx0d0f810a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f810a);
++ data->f2x9cx0d0fc002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc002);
++ data->f2x9cx0d0fc006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc006);
++ data->f2x9cx0d0fc00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00a);
++ data->f2x9cx0d0fc00e = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00e);
++ data->f2x9cx0d0fc012 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc012);
++
++ data->f2x9cx0d0f2031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2031);
++ data->f2x9cx0d0f2131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2131);
++ data->f2x9cx0d0f2231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2231);
++ data->f2x9cx0d0f8031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8031);
++ data->f2x9cx0d0f8131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8131);
++ data->f2x9cx0d0f8231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8231);
++ data->f2x9cx0d0fc031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc031);
++ data->f2x9cx0d0fc131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc131);
++ data->f2x9cx0d0fc231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc231);
++ for (i=0; i<9; i++)
++ data->f2x9cx0d0f0_0_f_31[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0031 | (i << 8));
++
++ data->f2x9cx0d0f8021 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8021);
++ }
+
+ /* Stage 4 */
+- data->f2x94 = pci_read_config32(dev_fn2, 0x94 + (0x100 * channel));
++ data->f2x94 = read_config32_dct(dev_fn2, node, channel, 0x94);
+
+ /* Stage 6 */
+ for (i=0; i<9; i++)
+ for (j=0; j<3; j++)
+- data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
+- data->f2x9cx00 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x00);
+- data->f2x9cx0a = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0a);
+- data->f2x9cx0c = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0c);
++ 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));
++ data->f2x9cx00 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x00);
++ data->f2x9cx0a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0a);
++ data->f2x9cx0c = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0c);
+
+ /* Stage 7 */
+- data->f2x9cx04 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x04);
++ data->f2x9cx04 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x04);
+
+ /* Stage 9 */
+- data->f2x9cx0d0fe006 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe006);
+- data->f2x9cx0d0fe007 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe007);
++ data->f2x9cx0d0fe006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe006);
++ data->f2x9cx0d0fe007 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe007);
+
+ /* Stage 10 */
+ for (i=0; i<12; i++)
+- data->f2x9cx10[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x10 + i);
++ data->f2x9cx10[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x10 + i);
+ for (i=0; i<12; i++)
+- data->f2x9cx20[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x20 + i);
++ data->f2x9cx20[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x20 + i);
+ for (i=0; i<4; i++)
+ for (j=0; j<3; j++)
+- data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j));
++ data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x01 + i) + (0x100 * j));
+ for (i=0; i<4; i++)
+ for (j=0; j<3; j++)
+- data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j));
+- data->f2x9cx0d = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d);
++ data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x05 + i) + (0x100 * j));
++ data->f2x9cx0d = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d);
+ for (i=0; i<9; i++)
+- data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8));
++ data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0013 | (i << 8));
+ for (i=0; i<9; i++)
+- data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8));
++ data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0030 | (i << 8));
+ for (i=0; i<4; i++)
+- data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8));
++ data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2030 | (i << 8));
+ for (i=0; i<2; i++)
+ for (j=0; j<3; j++)
+- data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
+- data->f2x9cx0d0f812f = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f812f);
++ data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
++ data->f2x9cx0d0f812f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f812f);
+
+ /* Stage 11 */
+ if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+ for (i=0; i<12; i++)
+- data->f2x9cx30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x30 + i);
++ data->f2x9cx30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x30 + i);
+ for (i=0; i<12; i++)
+- data->f2x9cx40[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x40 + i);
++ data->f2x9cx40[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x40 + i);
+ }
+
+ /* Other */
+@@ -212,6 +364,43 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+ }
+ }
+ #else
++static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
++ if (is_fam15h()) {
++ uint32_t dword;
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ reg += dct * 0x100;
++ }
++
++ pci_write_config32(dev, reg, value);
++}
++
++static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) {
++ uint32_t dword;
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++
++ /* Select NB Pstate index */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~(0x3 << 4);
++ dword |= (nb_pstate & 0x3) << 4;
++ pci_write_config32(dev_fn1, 0x10c, dword);
++
++ pci_write_config32(dev, reg, value);
++}
++
+ static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
+ {
+ uint32_t dword;
+@@ -223,6 +412,25 @@ static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, u
+ dword = pci_read_config32(dev, index_ctl_reg);
+ } while (!(dword & (1 << 31)));
+ }
++
++static 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)
++{
++ if (is_fam15h()) {
++ uint32_t dword;
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ index_ctl_reg += dct * 0x100;
++ }
++
++ return write_amd_dct_index_register(dev, index_ctl_reg, index, value);
++}
+ #endif
+
+ #ifdef __PRE_RAM__
+@@ -262,31 +470,31 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x40 + (0x100 * channel), data->f1x40);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x44 + (0x100 * channel), data->f1x44);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x48 + (0x100 * channel), data->f1x48);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x4c + (0x100 * channel), data->f1x4c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x50 + (0x100 * channel), data->f1x50);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x54 + (0x100 * channel), data->f1x54);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x58 + (0x100 * channel), data->f1x58);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x5c + (0x100 * channel), data->f1x5c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x60 + (0x100 * channel), data->f1x60);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x64 + (0x100 * channel), data->f1x64);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x68 + (0x100 * channel), data->f1x68);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x6c + (0x100 * channel), data->f1x6c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x70 + (0x100 * channel), data->f1x70);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x74 + (0x100 * channel), data->f1x74);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x78 + (0x100 * channel), data->f1x78);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x7c + (0x100 * channel), data->f1x7c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0xf0 + (0x100 * channel), data->f1xf0);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x120 + (0x100 * channel), data->f1x120);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x124 + (0x100 * channel), data->f1x124);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x10c + (0x100 * channel), data->f2x10c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x114 + (0x100 * channel), data->f2x114);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x118 + (0x100 * channel), data->f2x118);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x11c + (0x100 * channel), data->f2x11c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x1b0 + (0x100 * channel), data->f2x1b0);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x44 + (0x100 * channel), data->f3x44);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x40, data->f1x40);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x44, data->f1x44);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x48, data->f1x48);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x4c, data->f1x4c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x50, data->f1x50);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x54, data->f1x54);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x58, data->f1x58);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x5c, data->f1x5c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x60, data->f1x60);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x64, data->f1x64);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x68, data->f1x68);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x6c, data->f1x6c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x70, data->f1x70);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x74, data->f1x74);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x78, data->f1x78);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x7c, data->f1x7c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0xf0, data->f1xf0);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x120, data->f1x120);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44);
+ for (i=0; i<16; i++) {
+ wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]);
+ }
+@@ -313,31 +521,97 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
+- ganged = !!(data->f2x110 & 0x10);
++ if (is_fam15h())
++ ganged = 0;
++ else
++ ganged = !!(data->f2x110 & 0x10);
+ if ((ganged == 1) && (channel > 0))
+ continue;
+
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x40 + (0x100 * channel), data->f2x40);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x44 + (0x100 * channel), data->f2x44);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x48 + (0x100 * channel), data->f2x48);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x4c + (0x100 * channel), data->f2x4c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x50 + (0x100 * channel), data->f2x50);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x54 + (0x100 * channel), data->f2x54);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x58 + (0x100 * channel), data->f2x58);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x5c + (0x100 * channel), data->f2x5c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x60 + (0x100 * channel), data->f2x60);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x64 + (0x100 * channel), data->f2x64);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x68 + (0x100 * channel), data->f2x68);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x6c + (0x100 * channel), data->f2x6c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x78 + (0x100 * channel), data->f2x78);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x7c + (0x100 * channel), data->f2x7c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x80 + (0x100 * channel), data->f2x80);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x84 + (0x100 * channel), data->f2x84);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x88 + (0x100 * channel), data->f2x88);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x8c + (0x100 * channel), data->f2x8c);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), data->f2x90);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa4 + (0x100 * channel), data->f2xa4);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa8 + (0x100 * channel), data->f2xa8);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x40, data->f2x40);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x44, data->f2x44);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x48, data->f2x48);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x4c, data->f2x4c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x50, data->f2x50);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x54, data->f2x54);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x58, data->f2x58);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x5c, data->f2x5c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x60, data->f2x60);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x64, data->f2x64);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x68, data->f2x68);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x6c, data->f2x6c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x7c, data->f2x7c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x80, data->f2x80);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x84, data->f2x84);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x88, data->f2x88);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, data->f2x90);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa4, data->f2xa4);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa8, data->f2xa8);
++ }
++ }
++
++ /* Family 15h-specific configuration */
++ if (is_fam15h()) {
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ /* Initialize DCT */
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0000000b, 0x80000000);
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013);
++ dword &= ~0xffff;
++ dword |= 0x118;
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, dword);
++
++ /* Restore values */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x200, data->f2x200);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x204, data->f2x204);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x208, data->f2x208);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x20c, data->f2x20c);
++ for (i=0; i<4; i++)
++ write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x214, data->f2x214);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x218, data->f2x218);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x21c, data->f2x21c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x22c, data->f2x22c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x230, data->f2x230);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x234, data->f2x234);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x238, data->f2x238);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x23c, data->f2x23c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x240, data->f2x240);
++
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, data->f2x9cx0d0fe013);
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f001f | (i << 8), data->f2x9cx0d0f0_8_0_1f[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f201f, data->f2x9cx0d0f201f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f211f, data->f2x9cx0d0f211f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f221f, data->f2x9cx0d0f221f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f801f, data->f2x9cx0d0f801f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f811f, data->f2x9cx0d0f811f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f821f, data->f2x9cx0d0f821f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc01f, data->f2x9cx0d0fc01f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc11f, data->f2x9cx0d0fc11f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc21f, data->f2x9cx0d0fc21f);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f4009, data->f2x9cx0d0f4009);
++
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2031, data->f2x9cx0d0f2031);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2131, data->f2x9cx0d0f2131);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2231, data->f2x9cx0d0f2231);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8031, data->f2x9cx0d0f8031);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8131, data->f2x9cx0d0f8131);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8231, data->f2x9cx0d0f8231);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc031, data->f2x9cx0d0fc031);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc131, data->f2x9cx0d0fc131);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc231, data->f2x9cx0d0fc231);
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0031 | (i << 8), data->f2x9cx0d0f0_0_f_31[i]);
++
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8021, data->f2x9cx0d0f8021);
++ }
+ }
+ }
+
+@@ -348,33 +622,44 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
+- ganged = !!(data->f2x110 & 0x10);
++ if (is_fam15h())
++ ganged = 0;
++ else
++ ganged = !!(data->f2x110 & 0x10);
+ if ((ganged == 1) && (channel > 0))
+ continue;
+
+- /* Disable PHY auto-compensation engine */
+- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
+- if (!(dword & (1 << 30))) {
+- dword |= (1 << 30);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
+-
+- /* Wait for 5us */
+- mct_Wait(100);
++ if (is_fam15h()) {
++ /* Program PllLockTime = 0x190 */
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
++ dword &= ~0xffff;
++ dword |= 0x190;
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
++
++ /* Program MemClkFreqVal = 0 */
++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
++ dword &= (0x1 << 7);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, dword);
++
++ /* Restore DRAM Adddress/Timing Control Register */
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
++ } else {
++ /* Disable PHY auto-compensation engine */
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
++ if (!(dword & (1 << 30))) {
++ dword |= (1 << 30);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
++
++ /* Wait for 5us */
++ mct_Wait(100);
++ }
+ }
+
+ /* Restore DRAM Configuration High Register */
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x94 + (0x100 * channel), data->f2x94);
+-
+- /* Enable PHY auto-compensation engine */
+- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
+- dword &= ~(1 << 30);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, data->f2x94);
+ }
+ }
+
+- /* Wait for 750us */
+- mct_Wait(15000);
+-
+ /* Stage 5 */
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+ for (channel = 0; channel < 2; channel++) {
+@@ -382,17 +667,40 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
+- ganged = !!(data->f2x110 & 0x10);
++ if (is_fam15h())
++ ganged = 0;
++ else
++ ganged = !!(data->f2x110 & 0x10);
+ if ((ganged == 1) && (channel > 0))
+ continue;
+
++ dct_enabled = !(data->f2x94 & (1 << 14));
++ if (!dct_enabled)
++ continue;
++
+ /* Wait for any pending PHY frequency changes to complete */
+ do {
+- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
+ } while (dword & (1 << 21));
++
++ if (is_fam15h()) {
++ /* Program PllLockTime = 0xf */
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
++ dword &= ~0xffff;
++ dword |= 0xf;
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
++ } else {
++ /* Enable PHY auto-compensation engine */
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
++ dword &= ~(1 << 30);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
++ }
+ }
+ }
+
++ /* Wait for 750us */
++ mct_Wait(15000);
++
+ /* Stage 6 */
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+ for (channel = 0; channel < 2; channel++) {
+@@ -402,10 +710,49 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+
+ for (i=0; i<9; i++)
+ for (j=0; j<3; j++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x00, data->f2x9cx00);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0a, data->f2x9cx0a);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0c, data->f2x9cx0c);
++ 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]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x00, data->f2x9cx00);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0a, data->f2x9cx0a);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0c, data->f2x9cx0c);
++ }
++ }
++
++ /* Family 15h-specific configuration */
++ if (is_fam15h()) {
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003);
++ dword |= (0x3 << 13); /* DisAutoComp, DisablePredriverCal = 1 */
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, dword);
++
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0006 | (i << 8), data->f2x9cx0d0f0_8_0_06[i]);
++ for (i=0; i<9; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f000a | (i << 8), data->f2x9cx0d0f0_8_0_0a[i]);
++ for (i=0; i<9; i++)
++ 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]));
++
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8006, data->f2x9cx0d0f8006);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f800a, data->f2x9cx0d0f800a);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8106, data->f2x9cx0d0f8106);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f810a, data->f2x9cx0d0f810a);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc006, data->f2x9cx0d0fc006);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00a, data->f2x9cx0d0fc00a);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00e, data->f2x9cx0d0fc00e);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc012, data->f2x9cx0d0fc012);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8002, (0x8000 | data->f2x9cx0d0f8002));
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8102, (0x8000 | data->f2x9cx0d0f8102));
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc002, (0x8000 | data->f2x9cx0d0fc002));
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2002, (0x8000 | data->f2x9cx0d0f2002));
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2102, (0x8000 | data->f2x9cx0d0f2102));
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2202, (0x8000 | data->f2x9cx0d0f2202));
++
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, data->f2x9cx0d0fe003);
++ }
+ }
+ }
+
+@@ -416,11 +763,15 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
+- ganged = !!(data->f2x110 & 0x10);
++ if (is_fam15h())
++ ganged = 0;
++ else
++ ganged = !!(data->f2x110 & 0x10);
+ if ((ganged == 1) && (channel > 0))
+ continue;
+
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x04, data->f2x9cx04);
++ if (!is_fam15h())
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
+ }
+ }
+
+@@ -435,16 +786,19 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!dct_enabled)
+ continue;
+
+- ganged = !!(data->f2x110 & 0x10);
++ if (is_fam15h())
++ ganged = 0;
++ else
++ ganged = !!(data->f2x110 & 0x10);
+ if ((ganged == 1) && (channel > 0))
+ continue;
+
+ printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel);
+
+ /* Exit self refresh mode */
+- dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
+ dword |= (1 << 1);
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), dword);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, dword);
+ }
+ }
+
+@@ -463,12 +817,12 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+
+ /* Wait for transition from self refresh mode to complete */
+ do {
+- dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
+ } while (dword & (1 << 1));
+
+ /* Restore registers */
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe006, data->f2x9cx0d0fe006);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe007, data->f2x9cx0d0fe007);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, data->f2x9cx0d0fe006);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe007, data->f2x9cx0d0fe007);
+ }
+ }
+
+@@ -480,26 +834,26 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ continue;
+
+ for (i=0; i<12; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x10 + i, data->f2x9cx10[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
+ for (i=0; i<12; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x20 + i, data->f2x9cx20[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
+ for (i=0; i<4; i++)
+ for (j=0; j<3; j++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
+ for (i=0; i<4; i++)
+ for (j=0; j<3; j++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d, data->f2x9cx0d);
++ 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]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d, data->f2x9cx0d);
+ for (i=0; i<9; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
+ for (i=0; i<9; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
+ for (i=0; i<4; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
+ for (i=0; i<2; i++)
+ for (j=0; j<3; j++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]);
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f812f, data->f2x9cx0d0f812f);
++ 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]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f812f, data->f2x9cx0d0f812f);
+ }
+ }
+
+@@ -512,9 +866,9 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ continue;
+
+ for (i=0; i<12; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x30 + i, data->f2x9cx30[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
+ for (i=0; i<12; i++)
+- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x40 + i, data->f2x9cx40[i]);
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti.h b/src/northbridge/amd/amdmct/wrappers/mcti.h
+index 38e66e1..2aba377 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti.h
++++ b/src/northbridge/amd/amdmct/wrappers/mcti.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -62,10 +63,15 @@ UPDATE AS NEEDED
+ #endif
+
+ #ifndef MEM_MAX_LOAD_FREQ
+-#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+- #define MEM_MAX_LOAD_FREQ 800
+-#else
+- #define MEM_MAX_LOAD_FREQ 400
++#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
++ #define MEM_MAX_LOAD_FREQ 933
++ #define MEM_MIN_PLATFORM_FREQ_FAM10 400
++ #define MEM_MIN_PLATFORM_FREQ_FAM15 333
++#else /* AMD_FAM10_DDR2 */
++ #define MEM_MAX_LOAD_FREQ 400
++ #define MEM_MIN_PLATFORM_FREQ_FAM10 200
++ /* DDR2 not available on Family 15h */
++ #define MEM_MIN_PLATFORM_FREQ_FAM15 0
+ #endif
+ #endif
+
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index 47260f2..1d4eade 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -44,7 +44,7 @@
+ #define MINIMUM_DRAM_BELOW_4G 0x1000000
+
+ static const uint16_t ddr2_limits[4] = {400, 333, 266, 200};
+-static const uint16_t ddr3_limits[4] = {800, 666, 533, 400};
++static const uint16_t ddr3_limits[16] = {933, 800, 666, 533, 400, 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ static u16 mctGet_NVbits(u8 index)
+ {
+@@ -81,12 +81,19 @@ static u16 mctGet_NVbits(u8 index)
+ if (get_option(&nvram, "max_mem_clock") == CB_SUCCESS) {
+ int limit = val;
+ if (IS_ENABLED(CONFIG_DIMM_DDR3))
+- limit = ddr3_limits[nvram & 3];
++ limit = ddr3_limits[nvram & 0xf];
+ else if (IS_ENABLED(CONFIG_DIMM_DDR2))
+- limit = ddr2_limits[nvram & 3];
++ limit = ddr2_limits[nvram & 0x3];
+ val = min(limit, val);
+ }
+ break;
++ case NV_MIN_MEMCLK:
++ /* Minimum platform supported memclk */
++ if (is_fam15h())
++ val = MEM_MIN_PLATFORM_FREQ_FAM15;
++ else
++ val = MEM_MIN_PLATFORM_FREQ_FAM10;
++ break;
+ case NV_ECC_CAP:
+ #if SYSTEM_TYPE == SERVER
+ val = 1; /* memory bus ECC capable */
+@@ -254,6 +261,9 @@ static u16 mctGet_NVbits(u8 index)
+ case NV_L2BKScrub:
+ val = 0; /* Disabled - See L2Scrub in BKDG */
+ break;
++ case NV_L3BKScrub:
++ val = 0; /* Disabled - See L3Scrub in BKDG */
++ break;
+ case NV_DCBKScrub:
+ val = 0; /* Disabled - See DcacheScrub in BKDG */
+ break;
+@@ -299,10 +309,14 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ pDCTstat->PresetmaxFreq = mctGet_NVbits(NV_MAX_MEMCLK);
+
+ /* Determine the number of installed DIMMs */
++ uint8_t dimm;
+ int ch1_count = 0;
+ int ch2_count = 0;
+ uint8_t ch1_registered = 0;
+ uint8_t ch2_registered = 0;
++ uint8_t ch1_voltage = 0;
++ uint8_t ch2_voltage = 0;
++ uint8_t highest_rank_count[2];
+ int i;
+ for (i = 0; i < 15; i = i + 2) {
+ if (pDCTstat->DIMMValid & (1 << i))
+@@ -321,8 +335,26 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ printk(BIOS_DEBUG, "mctGet_MaxLoadFreq: Channel 2: %d DIMM(s) detected\n", ch2_count);
+ }
+
++#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
++ for (i = 0; i < 15; i = i + 2) {
++ if (pDCTstat->DIMMValid & (1 << i))
++ ch1_voltage |= pDCTstat->DimmConfiguredVoltage[i];
++ if (pDCTstat->DIMMValid & (1 << (i + 1)))
++ ch2_voltage |= pDCTstat->DimmConfiguredVoltage[i + 1];
++ }
++#endif
++
++ for (i = 0; i < 2; i++) {
++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[i];
++ highest_rank_count[i] = 0x0;
++ for (dimm = 0; dimm < 8; dimm++) {
++ if (pDCTData->DimmRanks[dimm] > highest_rank_count[i])
++ highest_rank_count[i] = pDCTData->DimmRanks[dimm];
++ }
++ }
++
+ /* Set limits if needed */
+- pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), (ch1_registered || ch2_registered), pDCTstat->PresetmaxFreq);
++ pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), max(highest_rank_count[0], highest_rank_count[1]), (ch1_registered || ch2_registered), (ch1_voltage | ch2_voltage), pDCTstat->PresetmaxFreq);
+ }
+
+ #ifdef UNUSED_CODE
+@@ -486,7 +518,7 @@ static void mctHookAfterAnyTraining(void)
+ {
+ }
+
+-static u32 mctGetLogicalCPUID_D(u8 node)
++static uint64_t mctGetLogicalCPUID_D(u8 node)
+ {
+ return mctGetLogicalCPUID(node);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0037-mainboard-asus-kgpe-d16-Add-initial-Family-15h-CPU-s.patch b/resources/libreboot/patch/kgpe-d16/0037-mainboard-asus-kgpe-d16-Add-initial-Family-15h-CPU-s.patch
new file mode 100644
index 00000000..0fbf1f47
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0037-mainboard-asus-kgpe-d16-Add-initial-Family-15h-CPU-s.patch
@@ -0,0 +1,563 @@
+From d1d919c400ef903930b558c50a4b1880717b2995 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 5 Sep 2015 19:37:57 -0500
+Subject: [PATCH 037/146] mainboard/asus/kgpe-d16: Add initial Family 15h CPU
+ support
+
+---
+ src/mainboard/asus/kgpe-d16/Kconfig | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.default | 3 +-
+ src/mainboard/asus/kgpe-d16/cmos.layout | 15 +-
+ src/mainboard/asus/kgpe-d16/devicetree.cb | 4 +
+ src/mainboard/asus/kgpe-d16/resourcemap.c | 276 ++++++++++++++++++++++++++++-
+ src/mainboard/asus/kgpe-d16/romstage.c | 58 +++---
+ 6 files changed, 326 insertions(+), 31 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index f9556fc..d0af47f 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -12,6 +12,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
+ select SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA
+ select SUPERIO_NUVOTON_NCT5572D
+ select PARALLEL_CPU_INIT
++ select HAVE_ROMSTAGE_CONSOLE_SPINLOCK
+ select HAVE_HARD_RESET
+ select HAVE_OPTION_TABLE
+ select HAVE_CMOS_DEFAULT
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 39a4778..3e2ea3a 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -2,13 +2,14 @@ baud_rate = 115200
+ debug_level = Spew
+ multi_core = Enable
+ slow_cpu = off
++compute_unit_siblings = Enable
+ iommu = Disable
+ nmi = Disable
+ hypertransport_speed_limit = Auto
+ max_mem_clock = DDR3-1600
+ minimum_memory_voltage = 1.5V
+ ECC_memory = Enable
+-ECC_redirection = Disable
++ECC_redirection = Enable
+ ecc_scrub_rate = 1.28us
+ interleave_chip_selects = Enable
+ interleave_nodes = Disable
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 110e0bb..e55edc4 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -31,8 +31,8 @@ entries
+ 401 1 e 1 interleave_chip_selects
+ 402 1 e 1 interleave_nodes
+ 403 1 e 1 interleave_memory_channels
+-404 2 e 8 max_mem_clock
+-406 1 e 2 multi_core
++404 4 e 8 max_mem_clock
++408 1 e 2 multi_core
+ 412 4 e 6 debug_level
+ 440 4 e 9 slow_cpu
+ 444 1 e 1 nmi
+@@ -42,6 +42,7 @@ entries
+ 457 1 e 1 ECC_redirection
+ 458 4 e 11 hypertransport_speed_limit
+ 462 2 e 12 minimum_memory_voltage
++464 1 e 2 compute_unit_siblings
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+@@ -79,10 +80,12 @@ enumerations
+ 6 6 Information
+ 6 7 Debug
+ 6 8 Spew
+-8 0 DDR3-1600
+-8 1 DDR3-1333
+-8 2 DDR3-1066
+-8 3 DDR3-800
++8 0 DDR3-1866
++8 1 DDR3-1600
++8 2 DDR3-1333
++8 3 DDR3-1066
++8 4 DDR3-800
++8 5 DDR3-667
+ 9 0 off
+ 9 1 87.5%
+ 9 2 75.0%
+diff --git a/src/mainboard/asus/kgpe-d16/devicetree.cb b/src/mainboard/asus/kgpe-d16/devicetree.cb
+index d0288da..9bff01e 100644
+--- a/src/mainboard/asus/kgpe-d16/devicetree.cb
++++ b/src/mainboard/asus/kgpe-d16/devicetree.cb
+@@ -228,21 +228,25 @@ chip northbridge/amd/amdfam10/root_complex # Root complex
+ device pci 18.2 on end
+ device pci 18.3 on end
+ device pci 18.4 on end
++ device pci 18.5 on end
+ device pci 19.0 on end # Socket 0 node 1
+ device pci 19.1 on end
+ device pci 19.2 on end
+ device pci 19.3 on end
+ device pci 19.4 on end
++ device pci 19.5 on end
+ device pci 1a.0 on end # Socket 1 node 0
+ device pci 1a.1 on end
+ device pci 1a.2 on end
+ device pci 1a.3 on end
+ device pci 1a.4 on end
++ device pci 1a.5 on end
+ device pci 1b.0 on end # Socket 1 node 1
+ device pci 1b.1 on end
+ device pci 1b.2 on end
+ device pci 1b.3 on end
+ device pci 1b.4 on end
++ device pci 1b.5 on end
+ end
+ end
+ end
+diff --git a/src/mainboard/asus/kgpe-d16/resourcemap.c b/src/mainboard/asus/kgpe-d16/resourcemap.c
+index 3e240dc..3aab8b8 100644
+--- a/src/mainboard/asus/kgpe-d16/resourcemap.c
++++ b/src/mainboard/asus/kgpe-d16/resourcemap.c
+@@ -23,7 +23,262 @@
+
+ static void setup_mb_resource_map(void)
+ {
+- static const unsigned int register_values[] = {
++ static const unsigned int fam15h_register_values[] = {
++ /* Careful set limit registers before base registers which contain the enables */
++ /* DRAM Limit i Registers
++ * F1:0x44 i = 0
++ * F1:0x4C i = 1
++ * F1:0x54 i = 2
++ * F1:0x5C i = 3
++ * F1:0x64 i = 4
++ * F1:0x6C i = 5
++ * F1:0x74 i = 6
++ * F1:0x7C i = 7
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 7: 3] Reserved
++ * [10: 8] Interleave select
++ * specifies the values of A[14:12] to use with interleave enable.
++ * [15:11] Reserved
++ * [31:16] DRAM Limit Address i Bits 39-24
++ * This field defines the upper address bits of a 40 bit address
++ * that define the end of the DRAM region.
++ */
++ // PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x44), 0x0000f8f8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x4C), 0x0000f8f8, 0x00000001,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x54), 0x0000f8f8, 0x00000002,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x5C), 0x0000f8f8, 0x00000003,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x64), 0x0000f8f8, 0x00000004,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x6C), 0x0000f8f8, 0x00000005,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x74), 0x0000f8f8, 0x00000006,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x7C), 0x0000f8f8, 0x00000007,
++
++ /* DRAM Base i Registers
++ * F1:0x40 i = 0
++ * F1:0x48 i = 1
++ * F1:0x50 i = 2
++ * F1:0x58 i = 3
++ * F1:0x60 i = 4
++ * F1:0x68 i = 5
++ * F1:0x70 i = 6
++ * F1:0x78 i = 7
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 7: 2] Reserved
++ * [10: 8] Interleave Enable
++ * 000 = No interleave
++ * 001 = Interleave on A[12] (2 nodes)
++ * 010 = reserved
++ * 011 = Interleave on A[12] and A[14] (4 nodes)
++ * 100 = reserved
++ * 101 = reserved
++ * 110 = reserved
++ * 111 = Interleve on A[12] and A[13] and A[14] (8 nodes)
++ * [15:11] Reserved
++ * [31:16] DRAM Base Address i Bits 39-24
++ * This field defines the upper address bits of a 40-bit address
++ * that define the start of the DRAM region.
++ */
++ // PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x40), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x48), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x50), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x58), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x60), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x68), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x70), 0x0000f8fc, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x78), 0x0000f8fc, 0x00000000,
++
++ /* Memory-Mapped I/O Limit i Registers
++ * F1:0x84 i = 0
++ * F1:0x8C i = 1
++ * F1:0x94 i = 2
++ * F1:0x9C i = 3
++ * F1:0xA4 i = 4
++ * F1:0xAC i = 5
++ * F1:0xB4 i = 6
++ * F1:0xBC i = 7
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 3: 3] Reserved
++ * [ 5: 4] Destination Link ID
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 = Link 3
++ * [ 6: 6] Reserved
++ * [ 7: 7] Non-Posted
++ * 0 = CPU writes may be posted
++ * 1 = CPU writes must be non-posted
++ * [31: 8] Memory-Mapped I/O Limit Address i (39-16)
++ * This field defines the upp adddress bits of a 40-bit address that
++ * defines the end of a memory-mapped I/O region n
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x84), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x8C), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x94), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x9C), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA4), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xAC), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB4), 0x00000048, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xBC), 0x00000048, 0x00000000,
++
++ /* Memory-Mapped I/O Base i Registers
++ * F1:0x80 i = 0
++ * F1:0x88 i = 1
++ * F1:0x90 i = 2
++ * F1:0x98 i = 3
++ * F1:0xA0 i = 4
++ * F1:0xA8 i = 5
++ * F1:0xB0 i = 6
++ * F1:0xB8 i = 7
++ * [ 0: 0] Read Enable
++ * 0 = Reads disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes disabled
++ * 1 = Writes Enabled
++ * [ 2: 2] Cpu Disable
++ * 0 = Cpu can use this I/O range
++ * 1 = Cpu requests do not use this I/O range
++ * [ 3: 3] Lock
++ * 0 = base/limit registers i are read/write
++ * 1 = base/limit registers i are read-only
++ * [ 7: 4] Reserved
++ * [31: 8] Memory-Mapped I/O Base Address i (39-16)
++ * This field defines the upper address bits of a 40bit address
++ * that defines the start of memory-mapped I/O region i
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x80), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x88), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x90), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0x98), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA0), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xA8), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB0), 0x000000f0, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xB8), 0x000000f0, 0x00000000,
++
++ /* PCI I/O Limit i Registers
++ * F1:0xC4 i = 0
++ * F1:0xCC i = 1
++ * F1:0xD4 i = 2
++ * F1:0xDC i = 3
++ * [ 2: 0] Destination Node ID
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 3: 3] Reserved
++ * [ 5: 4] Destination Link ID
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 = Link 3
++ * [11: 6] Reserved
++ * [24:12] PCI I/O Limit Address i
++ * This field defines the end of PCI I/O region n
++ * [31:25] Reserved
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC4), 0xFE000FC8, 0x00fff010, /* link 1 of cpu 0 --> AMD SR5690 */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xCC), 0xFE000FC8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD4), 0xFE000FC8, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xDC), 0xFE000FC8, 0x00000000,
++
++ /* PCI I/O Base i Registers
++ * F1:0xC0 i = 0
++ * F1:0xC8 i = 1
++ * F1:0xD0 i = 2
++ * F1:0xD8 i = 3
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 3: 2] Reserved
++ * [ 4: 4] VGA Enable
++ * 0 = VGA matches Disabled
++ * 1 = matches all address < 64K and where A[9:0] is in the
++ * range 3B0-3BB or 3C0-3DF independent of the base & limit registers
++ * [ 5: 5] ISA Enable
++ * 0 = ISA matches Disabled
++ * 1 = Blocks address < 64K and in the last 768 bytes of eack 1K block
++ * from matching agains this base/limit pair
++ * [11: 6] Reserved
++ * [24:12] PCI I/O Base i
++ * This field defines the start of PCI I/O region n
++ * [31:25] Reserved
++ */
++// PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC0), 0xFE000FCC, 0x00001013,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xC8), 0xFE000FCC, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD0), 0xFE000FCC, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xD8), 0xFE000FCC, 0x00000000,
++
++ /* Config Base and Limit i Registers
++ * F1:0xE0 i = 0
++ * F1:0xE4 i = 1
++ * F1:0xE8 i = 2
++ * F1:0xEC i = 3
++ * [ 0: 0] Read Enable
++ * 0 = Reads Disabled
++ * 1 = Reads Enabled
++ * [ 1: 1] Write Enable
++ * 0 = Writes Disabled
++ * 1 = Writes Enabled
++ * [ 2: 2] Device Number Compare Enable
++ * 0 = The ranges are based on bus number
++ * 1 = The ranges are ranges of devices on bus 0
++ * [ 3: 3] Reserved
++ * [ 6: 4] Destination Node
++ * 000 = Node 0
++ * 001 = Node 1
++ * 010 = Node 2
++ * 011 = Node 3
++ * 100 = Node 4
++ * 101 = Node 5
++ * 110 = Node 6
++ * 111 = Node 7
++ * [ 7: 7] Reserved
++ * [ 9: 8] Destination Link
++ * 00 = Link 0
++ * 01 = Link 1
++ * 10 = Link 2
++ * 11 - Link 3
++ * [15:10] Reserved
++ * [23:16] Bus Number Base i
++ * This field defines the lowest bus number in configuration region i
++ * [31:24] Bus Number Limit i
++ * This field defines the highest bus number in configuration region i
++ */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE0), 0x0000FC88, 0x05000103, /* link 1 of cpu 0 --> AMD SR5690 */
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE4), 0x0000FC88, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xE8), 0x0000FC88, 0x00000000,
++ PCI_ADDR(CONFIG_CBB, CONFIG_CDB, 1, 0xEC), 0x0000FC88, 0x00000000,
++
++ };
++
++ static const unsigned int fam10h_register_values[] = {
+ /* Careful set limit registers before base registers which contain the enables */
+ /* DRAM Limit i Registers
+ * F1:0x44 i = 0
+@@ -279,6 +534,21 @@ static void setup_mb_resource_map(void)
+ };
+
+ int max;
+- max = ARRAY_SIZE(register_values);
+- setup_resource_map(register_values, max);
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ if (fam15h) {
++ max = ARRAY_SIZE(fam15h_register_values);
++ setup_resource_map(fam15h_register_values, max);
++ } else {
++ max = ARRAY_SIZE(fam10h_register_values);
++ setup_resource_map(fam10h_register_values, max);
++ }
+ }
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 18e7c16..0df2447 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -42,6 +42,7 @@
+ #include <superio/nuvoton/common/nuvoton.h>
+ #include <superio/nuvoton/nct5572d/nct5572d.h>
+ #include <cpu/x86/bist.h>
++#include <smp/spinlock.h>
+ // #include "northbridge/amd/amdk8/incoherent_ht.c"
+ #include <southbridge/amd/sb700/sb700.h>
+ #include <southbridge/amd/sb700/smbus.h>
+@@ -202,20 +203,20 @@ void DIMMSetVoltages(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->NodePresent && (node & 0x1)) {
+ /* Set voltages */
+ if (allowed_voltages & 0x8) {
+- set_voltage = 1150;
++ set_voltage = 0x8;
+ set_ddr3_voltage(socket, 3);
+ } else if (allowed_voltages & 0x4) {
+- set_voltage = 1250;
++ set_voltage = 0x4;
+ set_ddr3_voltage(socket, 2);
+ } else if (allowed_voltages & 0x2) {
+- set_voltage = 1350;
++ set_voltage = 0x2;
+ set_ddr3_voltage(socket, 1);
+ } else {
+- set_voltage = 1500;
++ set_voltage = 0x1;
+ set_ddr3_voltage(socket, 0);
+ }
+
+- /* Save final DIMM voltages for SMBIOS use */
++ /* Save final DIMM voltages for MCT and SMBIOS use */
+ if (pDCTstat->NodePresent) {
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
+ pDCTstat->DimmConfiguredVoltage[dimm] = set_voltage;
+@@ -229,6 +230,9 @@ void DIMMSetVoltages(struct MCTStatStruc *pMCTstat,
+ }
+ }
+ }
++
++ /* Allow the DDR supply voltages to settle */
++ udelay(100000);
+ }
+
+ static void set_peripheral_control_lines(void) {
+@@ -288,6 +292,18 @@ static void execute_memory_test(void)
+ }
+ #endif
+
++static spinlock_t printk_spinlock CAR_GLOBAL;
++
++spinlock_t* romstage_console_lock(void)
++{
++ return car_get_var_ptr(&printk_spinlock);
++}
++
++void initialize_romstage_console_lock(void)
++{
++ car_get_var(printk_spinlock) = SPIN_LOCK_UNLOCKED;
++}
++
+ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
+ struct sys_info *sysinfo = &sysinfo_car;
+@@ -302,6 +318,9 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ int s3resume = acpi_is_wakeup_s3();
+
+ if (!cpu_init_detectedx && boot_cpu()) {
++ /* Initialize the printk spinlock */
++ initialize_romstage_console_lock();
++
+ /* Nothing special needs to be done to find bus 0 */
+ /* Allow the HT devices to be found */
+ set_bsp_node_CHtExtNodeCfgEn();
+@@ -354,7 +373,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+
+ post_code(0x33);
+
+- cpuSetAMDMSR();
++ cpuSetAMDMSR(0);
+ post_code(0x34);
+
+ amd_ht_init(sysinfo);
+@@ -368,18 +387,21 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ setup_mb_resource_map();
+ post_code(0x36);
+
+- /* wait for all the APs core0 started by finalize_node_setup. */
+- /* FIXME: A bunch of cores are going to start output to serial at once.
+- * It would be nice to fix up prink spinlocks for ROM XIP mode.
+- * I think it could be done by putting the spinlock flag in the cache
+- * of the BSP located right after sysinfo.
+- */
++ /* Wait for all the APs core0 started by finalize_node_setup. */
+ wait_all_core0_started();
+
+ /* run _early_setup before soft-reset. */
+ sr5650_early_setup();
+ sb7xx_51xx_early_setup();
+
++ if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
++ /* Core0 on each node is configured. Now setup any additional cores. */
++ printk(BIOS_DEBUG, "start_other_cores()\n");
++ start_other_cores();
++ post_code(0x37);
++ wait_all_other_cores_started(bsp_apicid);
++ }
++
+ if (IS_ENABLED(CONFIG_SET_FIDVID)) {
+ msr = rdmsr(0xc0010071);
+ printk(BIOS_DEBUG, "\nBegin FIDVID MSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
+@@ -389,11 +411,13 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+
+ post_code(0x39);
+
++ #if IS_ENABLED(CONFIG_SET_FIDVID)
+ if (!warm_reset_detect(0)) { // BSP is node 0
+ init_fidvid_bsp(bsp_apicid, sysinfo->nodes);
+ } else {
+ init_fidvid_stage2(bsp_apicid, 0); // BSP is node 0
+ }
++ #endif
+
+ post_code(0x3A);
+
+@@ -402,21 +426,13 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ printk(BIOS_DEBUG, "End FIDVIDMSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
+ }
+
+- if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
+- /* Core0 on each node is configured. Now setup any additional cores. */
+- printk(BIOS_DEBUG, "start_other_cores()\n");
+- start_other_cores();
+- post_code(0x37);
+- wait_all_other_cores_started(bsp_apicid);
+- }
+-
+ post_code(0x38);
+
+ init_timer(); // Need to use TMICT to synconize FID/VID
+
+ sr5650_htinit();
+
+- /* Reset for HT, FIDVID, PLL and errata changes to take affect. */
++ /* Reset for HT, FIDVID, PLL and errata changes to take effect. */
+ if (!warm_reset_detect(0)) {
+ printk(BIOS_INFO, "...WARM RESET...\n\n\n");
+ soft_reset();
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0038-amdmct-mct_ddr3-Disable-Fam10h-specific-MTRR-setup-o.patch b/resources/libreboot/patch/kgpe-d16/0038-amdmct-mct_ddr3-Disable-Fam10h-specific-MTRR-setup-o.patch
new file mode 100644
index 00000000..15adb858
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0038-amdmct-mct_ddr3-Disable-Fam10h-specific-MTRR-setup-o.patch
@@ -0,0 +1,36 @@
+From 59201912928ffcdbc4c7143548ceba490dea021a Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 1 Jun 2015 20:35:42 -0500
+Subject: [PATCH 038/146] amdmct/mct_ddr3: Disable Fam10h-specific MTRR setup
+ on Fam15h
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index c73cb26..3eb6b17 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1839,11 +1839,13 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+
+ if (nv_DQSTrainCTL) {
+ mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
+- /* TODO: should be in mctHookBeforeAnyTraining */
+- _WRMSR(0x26C, 0x04040404, 0x04040404);
+- _WRMSR(0x26D, 0x04040404, 0x04040404);
+- _WRMSR(0x26E, 0x04040404, 0x04040404);
+- _WRMSR(0x26F, 0x04040404, 0x04040404);
++ if (!is_fam15h()) {
++ /* TODO: should be in mctHookBeforeAnyTraining */
++ _WRMSR(0x26C, 0x04040404, 0x04040404);
++ _WRMSR(0x26D, 0x04040404, 0x04040404);
++ _WRMSR(0x26E, 0x04040404, 0x04040404);
++ _WRMSR(0x26F, 0x04040404, 0x04040404);
++ }
+ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass);
+
+ if (is_fam15h()) {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0039-cpu-amd-car-Add-romstage-BSP-stack-overrun-detection.patch b/resources/libreboot/patch/kgpe-d16/0039-cpu-amd-car-Add-romstage-BSP-stack-overrun-detection.patch
new file mode 100644
index 00000000..df6dee93
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0039-cpu-amd-car-Add-romstage-BSP-stack-overrun-detection.patch
@@ -0,0 +1,57 @@
+From a3709a31c062419544a607dfbca9a18773dad673 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 1 Jun 2015 23:58:59 -0500
+Subject: [PATCH 039/146] cpu/amd/car: Add romstage BSP stack overrun
+ detection
+
+---
+ src/cpu/amd/car/cache_as_ram.inc | 6 +++++-
+ src/cpu/amd/car/post_cache_as_ram.c | 8 ++++++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index ec70f67..9b29932 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -24,7 +24,7 @@
+ #include <cpu/amd/mtrr.h>
+
+ #define CacheSize CONFIG_DCACHE_RAM_SIZE
+-#define CacheBase (0xd0000 - CacheSize)
++#define CacheBase CONFIG_DCACHE_RAM_BASE
+ #define CacheSizeBSPStack CONFIG_DCACHE_BSP_STACK_SIZE
+ #define CacheSizeBSPSlush CONFIG_DCACHE_BSP_STACK_SLUSH
+
+@@ -473,6 +473,10 @@ fam10_end_part1:
+ movl $(CacheBase + CacheSize), %eax
+ movl %eax, %esp
+
++ /* Poison the lower stack boundary */
++ movl $((CacheBase + CacheSize) - CacheSizeBSPStack), %eax
++ movl $0xdeadbeef, (%eax)
++
+ post_code(0xa3)
+
+ jmp CAR_FAM10_ap_out
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index e7a41e5..b4d185e 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -110,6 +110,14 @@ void post_cache_as_ram(void)
+ void *resume_backup_memory = NULL;
+ uint32_t family = amd_fam1x_cpu_family();
+
++ /* Verify that the BSP didn't overrun the lower stack
++ * boundary during romstage execution
++ */
++ volatile uint32_t *lower_stack_boundary;
++ lower_stack_boundary = (void *)((CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE) - CONFIG_STACK_SIZE);
++ if ((*lower_stack_boundary) != 0xdeadbeef)
++ printk(BIOS_WARNING, "BSP overran lower stack boundary. Undefined behaviour may result!\n");
++
+ struct romstage_handoff *handoff;
+ handoff = romstage_handoff_find_or_add();
+ if (handoff != NULL)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0040-cpu-amd-car-Increase-Family-10h-CAR-size-limit-to-12.patch b/resources/libreboot/patch/kgpe-d16/0040-cpu-amd-car-Increase-Family-10h-CAR-size-limit-to-12.patch
new file mode 100644
index 00000000..66fcd626
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0040-cpu-amd-car-Increase-Family-10h-CAR-size-limit-to-12.patch
@@ -0,0 +1,28 @@
+From e27fed2f630fcc720bed48180ed8a527d4bcd5ce Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 2 Jun 2015 13:25:23 -0500
+Subject: [PATCH 040/146] cpu/amd/car: Increase Family 10h CAR size limit to
+ 128k
+
+This resolves issues with 4-node (32-core) systems not having
+sufficient CAR memory to boot.
+---
+ src/cpu/amd/car/post_cache_as_ram.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index b4d185e..503a666 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -114,7 +114,7 @@ void post_cache_as_ram(void)
+ * boundary during romstage execution
+ */
+ volatile uint32_t *lower_stack_boundary;
+- lower_stack_boundary = (void *)((CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE) - CONFIG_STACK_SIZE);
++ lower_stack_boundary = (void *)((CONFIG_DCACHE_RAM_BASE + CONFIG_DCACHE_RAM_SIZE) - CONFIG_DCACHE_BSP_STACK_SIZE);
+ if ((*lower_stack_boundary) != 0xdeadbeef)
+ printk(BIOS_WARNING, "BSP overran lower stack boundary. Undefined behaviour may result!\n");
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0041-cpu-amd-car-Move-AP-stacks-below-the-BSP-stack-to-fr.patch b/resources/libreboot/patch/kgpe-d16/0041-cpu-amd-car-Move-AP-stacks-below-the-BSP-stack-to-fr.patch
new file mode 100644
index 00000000..f0404feb
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0041-cpu-amd-car-Move-AP-stacks-below-the-BSP-stack-to-fr.patch
@@ -0,0 +1,30 @@
+From a499ee73bf8abfdc91a2118f0de29e5625ed39a4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 2 Jun 2015 20:18:44 -0500
+Subject: [PATCH 041/146] cpu/amd/car: Move AP stacks below the BSP stack to
+ free up space
+
+Caching SPD data during startup requires additional CAR space.
+There was a large chunk of free space between the AP stack top and
+the BSP stack bottom; moving the AP stacks below the BSP stack
+allows this space to be utilized.
+---
+ src/cpu/amd/model_10xxx/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/model_10xxx/Kconfig b/src/cpu/amd/model_10xxx/Kconfig
+index ebd282a..79db42a 100644
+--- a/src/cpu/amd/model_10xxx/Kconfig
++++ b/src/cpu/amd/model_10xxx/Kconfig
+@@ -32,7 +32,7 @@ config DCACHE_RAM_SIZE
+
+ config DCACHE_BSP_STACK_SIZE
+ hex
+- default 0x2000
++ default 0x4000
+
+ config DCACHE_BSP_STACK_SLUSH
+ hex
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0042-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch b/resources/libreboot/patch/kgpe-d16/0042-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch
new file mode 100644
index 00000000..d7083a15
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0042-northbridge-amd-amdmct-Read-SPD-data-into-cache-to-d.patch
@@ -0,0 +1,459 @@
+From 8eaa58398aa389dec27dd8290fcd8102fa27daa2 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 2 Jun 2015 20:51:59 -0500
+Subject: [PATCH 042/146] northbridge/amd/amdmct: Read SPD data into cache to
+ decrease bootup time
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 161 ++++++++++++++-------------
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 7 ++
+ 2 files changed, 92 insertions(+), 76 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 3eb6b17..b79b6c9 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -180,7 +180,7 @@ static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+ static void SyncSetting(struct DCTStatStruc *pDCTstat);
+-static u8 crcCheck(u8 smbaddr);
++static uint8_t crcCheck(struct DCTStatStruc *pDCTstat, uint8_t dimm);
+ static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
+ static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
+
+@@ -1170,6 +1170,20 @@ static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCT
+ precise_ndelay_fam15(pMCTstat, delay_ns);
+ }
+
++static void read_spd_bytes(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dimm)
++{
++ uint16_t addr;
++ uint16_t byte;
++
++ addr = Get_DIMMAddress_D(pDCTstat, dimm);
++ pDCTstat->spd_data.spd_address[dimm] = addr;
++
++ for (byte = 0; byte < 256; byte++) {
++ pDCTstat->spd_data.spd_bytes[dimm][byte] = mctRead_SPD(addr, byte);
++ }
++}
++
+ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1273,7 +1287,7 @@ restartinit:
+ mct_InitialMCT_D(pMCTstat, pDCTstat);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+- mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node */
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_preInitDCT\n");
+ mct_preInitDCT(pMCTstat, pDCTstat);
+@@ -2432,7 +2446,6 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ u32 dword;
+ u32 dev;
+ u32 val;
+- u16 smbaddr;
+
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+@@ -2452,64 +2465,62 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) {
+ LDIMM = i >> 1;
+ if (pDCTstat->DIMMValid & (1 << i)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+-
+- val = mctRead_SPD(smbaddr, SPD_MTBDivisor); /* MTB=Dividend/Divisor */
+- MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDivisor]; /* MTB=Dividend/Divisor */
++ MTB16x = ((pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDividend] & 0xff) << 4);
+ MTB16x /= val; /* transfer to MTB*16 */
+
+- byte = mctRead_SPD(smbaddr, SPD_tRPmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRPmin];
+ val = byte * MTB16x;
+ if (Trp < val)
+ Trp = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRRDmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRRDmin];
+ val = byte * MTB16x;
+ if (Trrd < val)
+ Trrd = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRCDmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCDmin];
+ val = byte * MTB16x;
+ if (Trcd < val)
+ Trcd = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tRTPmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRTPmin];
+ val = byte * MTB16x;
+ if (Trtp < val)
+ Trtp = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tWRmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tWRmin];
+ val = byte * MTB16x;
+ if (Twr < val)
+ Twr = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_tWTRmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tWTRmin];
+ val = byte * MTB16x;
+ if (Twtr < val)
+ Twtr = val;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xFF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xFF;
+ val >>= 4;
+ val <<= 8;
+- val |= mctRead_SPD(smbaddr, SPD_tRCmin) & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCmin] & 0xFF;
+ val *= MTB16x;
+ if (Trc < val)
+ Trc = val;
+
+- byte = mctRead_SPD(smbaddr, SPD_Density) & 0xF;
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Density] & 0xF;
+ if (Trfc[LDIMM] < byte)
+ Trfc[LDIMM] = byte;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xF;
+ val <<= 8;
+- val |= (mctRead_SPD(smbaddr, SPD_tRASmin) & 0xFF);
++ val |= (pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRASmin] & 0xFF);
+ val *= MTB16x;
+ if (Tras < val)
+ Tras = val;
+
+- val = mctRead_SPD(smbaddr, SPD_Upper_tFAW) & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tFAW] & 0xF;
+ val <<= 8;
+- val |= mctRead_SPD(smbaddr, SPD_tFAWmin) & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tFAWmin] & 0xFF;
+ val *= MTB16x;
+ if (Tfaw < val)
+ Tfaw = val;
+@@ -2925,7 +2936,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ u8 CLactual, CLdesired, CLT_Fail;
+ uint16_t min_frequency_tck16x;
+
+- u8 smbaddr, byte = 0, bytex = 0;
++ u8 byte = 0, bytex = 0;
+
+ CASLatLow = 0xFF;
+ CASLatHigh = 0xFF;
+@@ -2946,28 +2957,27 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+
+ for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
+ if (pDCTstat->DIMMValid & (1 << i)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+ /* Step 1: Determine the common set of supported CAS Latency
+ * values for all modules on the memory channel using the CAS
+ * Latencies Supported in SPD bytes 14 and 15.
+ */
+- byte = mctRead_SPD(smbaddr, SPD_CASLow);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_CASLow];
+ CASLatLow &= byte;
+- byte = mctRead_SPD(smbaddr, SPD_CASHigh);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_CASHigh];
+ CASLatHigh &= byte;
+ /* Step 2: Determine tAAmin(all) which is the largest tAAmin
+ value for all modules on the memory channel (SPD byte 16). */
+- byte = mctRead_SPD(smbaddr, SPD_MTBDivisor);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDivisor];
+
+- MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
++ MTB16x = ((pDCTstat->spd_data.spd_bytes[dct + i][SPD_MTBDividend] & 0xFF)<<4);
+ MTB16x /= byte; /* transfer to MTB*16 */
+
+- byte = mctRead_SPD(smbaddr, SPD_tAAmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tAAmin];
+ if (tAAmin16x < byte * MTB16x)
+ tAAmin16x = byte * MTB16x;
+ /* Step 3: Determine tCKmin(all) which is the largest tCKmin
+ value for all modules on the memory channel (SPD byte 12). */
+- byte = mctRead_SPD(smbaddr, SPD_tCKmin);
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_tCKmin];
+
+ if (tCKmin16x < byte * MTB16x)
+ tCKmin16x = byte * MTB16x;
+@@ -3338,7 +3348,6 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ u8 byte;
+ u16 word;
+ u32 dword;
+- u16 smbaddr;
+
+ dev = pDCTstat->dev_dct;
+
+@@ -3349,16 +3358,14 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ byte -= 3;
+
+ if (pDCTstat->DIMMValid & (1<<byte)) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, (ChipSel + dct));
+-
+- byte = mctRead_SPD(smbaddr, SPD_Addressing);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Addressing];
+ Rows = (byte >> 3) & 0x7; /* Rows:0b=12-bit,... */
+ Cols = byte & 0x7; /* Cols:0b=9-bit,... */
+
+- byte = mctRead_SPD(smbaddr, SPD_Density);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Density];
+ Banks = (byte >> 4) & 7; /* Banks:0b=3-bit,... */
+
+- byte = mctRead_SPD(smbaddr, SPD_Organization);
++ byte = pDCTstat->spd_data.spd_bytes[ChipSel + dct][SPD_Organization];
+ Ranks = ((byte >> 3) & 7) + 1;
+
+ /* Configure Bank encoding
+@@ -3453,46 +3460,42 @@ static void SPDCalcWidth_D(struct MCTStatStruc *pMCTstat,
+ * and determine the width mode: 64-bit, 64-bit muxed, 128-bit.
+ */
+ u8 i;
+- u8 smbaddr, smbaddr1;
+ u8 byte, byte1;
+
+ /* Check Symmetry of Channel A and Channel B DIMMs
+ (must be matched for 128-bit mode).*/
+ for (i=0; i < MAX_DIMMS_SUPPORTED; i += 2) {
+ if ((pDCTstat->DIMMValid & (1 << i)) && (pDCTstat->DIMMValid & (1<<(i+1)))) {
+- smbaddr = Get_DIMMAddress_D(pDCTstat, i);
+- smbaddr1 = Get_DIMMAddress_D(pDCTstat, i+1);
+-
+- byte = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Addressing) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x7;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Addressing] & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_Density) & 0x0f;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Density) & 0x0f;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Density] & 0x0f;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Density] & 0x0f;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_Organization) & 0x7;
+- byte1 = mctRead_SPD(smbaddr1, SPD_Organization) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x7;
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_Organization] & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3) & 0x7;
+- byte1 = (mctRead_SPD(smbaddr1, SPD_Organization) >> 3) & 0x7;
++ byte = (pDCTstat->spd_data.spd_bytes[i][SPD_Organization] >> 3) & 0x7;
++ byte1 = (pDCTstat->spd_data.spd_bytes[i + 1][SPD_Organization] >> 3) & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+- byte = mctRead_SPD(smbaddr, SPD_DMBANKS) & 7; /* #ranks-1 */
+- byte1 = mctRead_SPD(smbaddr1, SPD_DMBANKS) & 7; /* #ranks-1 */
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_DMBANKS] & 7; /* #ranks-1 */
++ byte1 = pDCTstat->spd_data.spd_bytes[i + 1][SPD_DMBANKS] & 7; /* #ranks-1 */
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+@@ -3673,8 +3676,9 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ status = mctRead_SPD(smbaddr, SPD_ByteUse);
+ if (status >= 0) { /* SPD access is ok */
+ pDCTstat->DIMMPresent |= 1 << i;
+- if (crcCheck(smbaddr)) { /* CRC is OK */
+- byte = mctRead_SPD(smbaddr, SPD_TYPE);
++ read_spd_bytes(pMCTstat, pDCTstat, i);
++ if (crcCheck(pDCTstat, i)) { /* CRC is OK */
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM) {
+ /*Dimm is 'Present'*/
+ pDCTstat->DIMMValid |= 1 << i;
+@@ -3687,36 +3691,41 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ /*if NV_SPDCHK_RESTRT is set to 1, ignore faulty SPD checksum*/
+ pDCTstat->ErrStatus |= 1<<SB_DIMMChkSum;
+- byte = mctRead_SPD(smbaddr, SPD_TYPE);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM)
+ pDCTstat->DIMMValid |= 1 << i;
+ }
+ }
++
++ /* Zero DIMM SPD data cache if DIMM not present / valid */
++ if (!(pDCTstat->DIMMValid & (1 << i)))
++ memset(pDCTstat->spd_data.spd_bytes[i], 0, 256);
++
+ /* Get module information for SMBIOS */
+ if (pDCTstat->DIMMValid & (1 << i)) {
+ pDCTstat->DimmManufacturerID[i] = 0;
+ for (k = 0; k < 8; k++)
+- pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
++ pDCTstat->DimmManufacturerID[i] |= ((uint64_t)pDCTstat->spd_data.spd_bytes[i][SPD_MANID_START + k]) << (k * 8);
+ for (k = 0; k < SPD_PARTN_LENGTH; k++)
+- pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
++ pDCTstat->DimmPartNumber[i][k] = pDCTstat->spd_data.spd_bytes[i][SPD_PARTN_START + k];
+ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
+ pDCTstat->DimmRevisionNumber[i] = 0;
+ for (k = 0; k < 2; k++)
+- pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
++ pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)pDCTstat->spd_data.spd_bytes[i][SPD_REVNO_START + k]) << (k * 8);
+ pDCTstat->DimmSerialNumber[i] = 0;
+ for (k = 0; k < 4; k++)
+- pDCTstat->DimmSerialNumber[i] |= ((uint32_t)mctRead_SPD(smbaddr, SPD_SERIAL_START + k)) << (k * 8);
+- pDCTstat->DimmRows[i] = (mctRead_SPD(smbaddr, SPD_Addressing) & 0x38) >> 3;
+- pDCTstat->DimmCols[i] = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7;
+- pDCTstat->DimmRanks[i] = ((mctRead_SPD(smbaddr, SPD_Organization) & 0x38) >> 3) + 1;
+- pDCTstat->DimmBanks[i] = 1ULL << (((mctRead_SPD(smbaddr, SPD_Density) & 0x70) >> 4) + 3);
+- pDCTstat->DimmWidth[i] = 1ULL << ((mctRead_SPD(smbaddr, SPD_BusWidth) & 0x7) + 3);
++ pDCTstat->DimmSerialNumber[i] |= ((uint32_t)pDCTstat->spd_data.spd_bytes[i][SPD_SERIAL_START + k]) << (k * 8);
++ pDCTstat->DimmRows[i] = (pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x38) >> 3;
++ pDCTstat->DimmCols[i] = pDCTstat->spd_data.spd_bytes[i][SPD_Addressing] & 0x7;
++ pDCTstat->DimmRanks[i] = ((pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x38) >> 3) + 1;
++ pDCTstat->DimmBanks[i] = 1ULL << (((pDCTstat->spd_data.spd_bytes[i][SPD_Density] & 0x70) >> 4) + 3);
++ pDCTstat->DimmWidth[i] = 1ULL << ((pDCTstat->spd_data.spd_bytes[i][SPD_BusWidth] & 0x7) + 3);
+ }
+ /* Check supported voltage(s) */
+- pDCTstat->DimmSupportedVoltages[i] = mctRead_SPD(smbaddr, SPD_Voltage) & 0x7;
++ pDCTstat->DimmSupportedVoltages[i] = pDCTstat->spd_data.spd_bytes[i][SPD_Voltage] & 0x7;
+ pDCTstat->DimmSupportedVoltages[i] ^= 0x1; /* Invert LSB to convert from SPD format to internal bitmap format */
+ /* Check module type */
+- byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE) & 0x7;
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_DIMMTYPE] & 0x7;
+ if (byte == JED_RDIMM || byte == JED_MiniRDIMM) {
+ RegDIMMPresent |= 1 << i;
+ pDCTstat->DimmRegistered[i] = 1;
+@@ -3730,13 +3739,13 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->DimmLoadReduced[i] = 0;
+ }
+ /* Check ECC capable */
+- byte = mctRead_SPD(smbaddr, SPD_BusWidth);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_BusWidth];
+ if (byte & JED_ECC) {
+ /* DIMM is ECC capable */
+ pDCTstat->DimmECCPresent |= 1 << i;
+ }
+ /* Check if x4 device */
+- devwidth = mctRead_SPD(smbaddr, SPD_Organization) & 0x7; /* 0:x4,1:x8,2:x16 */
++ devwidth = pDCTstat->spd_data.spd_bytes[i][SPD_Organization] & 0x7; /* 0:x4,1:x8,2:x16 */
+ if (devwidth == 0) {
+ /* DIMM is made with x4 or x16 drams */
+ pDCTstat->Dimmx4Present |= 1 << i;
+@@ -3746,7 +3755,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->Dimmx16Present |= 1 << i;
+ }
+
+- byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3);
++ byte = (pDCTstat->spd_data.spd_bytes[i][SPD_Organization] >> 3);
+ byte &= 7;
+ if (byte == 3) { /* 4ranks */
+ /* if any DIMMs are QR, we have to make two passes through DIMMs*/
+@@ -3781,7 +3790,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+
+ /* check address mirror support for unbuffered dimm */
+ /* check number of registers on a dimm for registered dimm */
+- byte = mctRead_SPD(smbaddr, SPD_AddressMirror);
++ byte = pDCTstat->spd_data.spd_bytes[i][SPD_AddressMirror];
+ if (RegDIMMPresent & (1 << i)) {
+ if ((byte & 3) > 1)
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+@@ -3790,20 +3799,20 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+ }
+ /* Get byte62: Reference Raw Card information. We dont need it now. */
+- /* byte = mctRead_SPD(smbaddr, SPD_RefRawCard); */
++ /* byte = pDCTstat->spd_data.spd_bytes[i][SPD_RefRawCard]; */
+ /* Get Byte65/66 for register manufacture ID code */
+- if ((0x97 == mctRead_SPD(smbaddr, SPD_RegManufactureID_H)) &&
+- (0x80 == mctRead_SPD(smbaddr, SPD_RegManufactureID_L))) {
+- if (0x16 == mctRead_SPD(smbaddr, SPD_RegManRevID))
++ if ((0x97 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManufactureID_H]) &&
++ (0x80 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManufactureID_L])) {
++ if (0x16 == pDCTstat->spd_data.spd_bytes[i][SPD_RegManRevID])
+ pDCTstat->RegMan2Present |= 1 << i;
+ else
+ pDCTstat->RegMan1Present |= 1 << i;
+ }
+ /* Get Control word values for RC3. We dont need it. */
+- byte = mctRead_SPD(smbaddr, 70);
++ byte = pDCTstat->spd_data.spd_bytes[i][70];
+ pDCTstat->CtrlWrd3 |= (byte >> 4) << (i << 2); /* C3 = SPD byte 70 [7:4] */
+ /* Get Control word values for RC4, and RC5 */
+- byte = mctRead_SPD(smbaddr, 71);
++ byte = pDCTstat->spd_data.spd_bytes[i][71];
+ pDCTstat->CtrlWrd4 |= (byte & 0xFF) << (i << 2); /* RC4 = SPD byte 71 [3:0] */
+ pDCTstat->CtrlWrd5 |= (byte >> 4) << (i << 2); /* RC5 = SPD byte 71 [7:4] */
+ }
+@@ -6182,14 +6191,14 @@ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
+ * 1010 001111 16 3 10 4GB
+ * 1011 010111 16 3 11 8GB
+ */
+-u8 crcCheck(u8 smbaddr)
++uint8_t crcCheck(struct DCTStatStruc *pDCTstat, uint8_t dimm)
+ {
+ u8 byte_use;
+ u8 Index;
+ u16 CRC;
+ u8 byte, i;
+
+- byte_use = mctRead_SPD(smbaddr, SPD_ByteUse);
++ byte_use = pDCTstat->spd_data.spd_bytes[dimm][SPD_ByteUse];
+ if (byte_use & 0x80)
+ byte_use = 117;
+ else
+@@ -6197,7 +6206,7 @@ u8 crcCheck(u8 smbaddr)
+
+ CRC = 0;
+ for (Index = 0; Index < byte_use; Index ++) {
+- byte = mctRead_SPD(smbaddr, Index);
++ byte = pDCTstat->spd_data.spd_bytes[dimm][Index];
+ CRC ^= byte << 8;
+ for (i=0; i<8; i++) {
+ if (CRC & 0x8000) {
+@@ -6207,5 +6216,5 @@ u8 crcCheck(u8 smbaddr)
+ CRC <<= 1;
+ }
+ }
+- return CRC == (mctRead_SPD(smbaddr, SPD_byte_127) << 8 | mctRead_SPD(smbaddr, SPD_byte_126));
++ return CRC == (pDCTstat->spd_data.spd_bytes[dimm][SPD_byte_127] << 8 | pDCTstat->spd_data.spd_bytes[dimm][SPD_byte_126]);
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 50fbff7..5bb09b4 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -322,6 +322,11 @@ struct MCTStatStruc {
+ ===============================================================================*/
+ #include "mwlc_d.h" /* I have to */
+
++struct amd_spd_node_data {
++ uint8_t spd_bytes[MAX_DIMMS_SUPPORTED][256]; /* [DIMM][byte] */
++ uint8_t spd_address[MAX_DIMMS_SUPPORTED]; /* [DIMM] */
++} __attribute__((packed));
++
+ struct DCTStatStruc { /* A per Node structure*/
+ /* DCTStatStruct_F - start */
+ u8 Node_ID; /* Node ID of current controller */
+@@ -615,6 +620,8 @@ struct DCTStatStruc { /* A per Node structure*/
+ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
+ uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
+ uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
++
++ struct amd_spd_node_data spd_data;
+ } __attribute__((packed));
+
+ struct amd_s3_persistent_mct_channel_data {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0043-cpu-amd-car-Initialize-entire-CAR-space-instead-of-o.patch b/resources/libreboot/patch/kgpe-d16/0043-cpu-amd-car-Initialize-entire-CAR-space-instead-of-o.patch
new file mode 100644
index 00000000..d8115535
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0043-cpu-amd-car-Initialize-entire-CAR-space-instead-of-o.patch
@@ -0,0 +1,32 @@
+From 5db1eb01a5e5a0ae52f25f648a0a4df0b8b02d78 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 4 Jun 2015 00:07:05 -0500
+Subject: [PATCH 043/146] cpu/amd/car: Initialize entire CAR space instead of
+ only half
+
+---
+ src/cpu/amd/car/cache_as_ram.inc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 9b29932..9c71270 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -460,12 +460,12 @@ fam10_end_part1:
+ /* Read the range with lodsl. */
+ cld
+ movl $CacheBase, %esi
+- movl $(CacheSize >> 2), %ecx
++ movl $CacheSize, %ecx
+ rep lodsl
+
+ /* Clear the range. */
+ movl $CacheBase, %edi
+- movl $(CacheSize >> 2), %ecx
++ movl $CacheSize, %ecx
+ xorl %eax, %eax
+ rep stosl
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Improve-SPD-DIMM-detect-reliabil.patch b/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Improve-SPD-DIMM-detect-reliabil.patch
new file mode 100644
index 00000000..4b38efd1
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0044-amd-amdmct-mct_ddr3-Improve-SPD-DIMM-detect-reliabil.patch
@@ -0,0 +1,47 @@
+From a1a3d844b273e794df56059cb15c9d22fc5fd087 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 4 Jun 2015 00:10:03 -0500
+Subject: [PATCH 044/146] amd/amdmct/mct_ddr3: Improve SPD DIMM detect
+ reliability
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index b79b6c9..771d743 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -3654,6 +3654,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ u8 devwidth;
+ u16 DimmSlots;
+ u8 byte = 0, bytex;
++ uint8_t crc_status;
+
+ /* preload data structure with addrs */
+ mctGet_DIMMAddr(pDCTstat, pDCTstat->Node_ID);
+@@ -3674,10 +3675,20 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ int status;
+ smbaddr = Get_DIMMAddress_D(pDCTstat, i);
+ status = mctRead_SPD(smbaddr, SPD_ByteUse);
++ if (status >= 0) {
++ /* Verify result */
++ status = mctRead_SPD(smbaddr, SPD_ByteUse);
++ }
+ if (status >= 0) { /* SPD access is ok */
+ pDCTstat->DIMMPresent |= 1 << i;
+ read_spd_bytes(pMCTstat, pDCTstat, i);
+- if (crcCheck(pDCTstat, i)) { /* CRC is OK */
++ crc_status = crcCheck(pDCTstat, i);
++ if (!crc_status) {
++ /* Try again in case there was a transient glitch */
++ read_spd_bytes(pMCTstat, pDCTstat, i);
++ crc_status = crcCheck(pDCTstat, i);
++ }
++ if (crc_status) { /* CRC is OK */
+ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM) {
+ /*Dimm is 'Present'*/
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0045-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch b/resources/libreboot/patch/kgpe-d16/0045-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch
new file mode 100644
index 00000000..52ba598f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0045-amd-amdmct-mct_ddr3-Use-training-values-from-previou.patch
@@ -0,0 +1,769 @@
+From 9874b958db2c994d3827af323bd6ab4c7306da23 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 4 Jun 2015 00:11:03 -0500
+Subject: [PATCH 045/146] amd/amdmct/mct_ddr3: Use training values from
+ previous boot if possible
+
+DRAM training accounts for most of the romstage startup time, yet
+if the hardware configuration has not changed from the previous boot
+the previously discovered training values are still valid. Use them
+if the DIMM configuration has not changed since the last boot.
+---
+ src/mainboard/asus/kgpe-d16/cmos.layout | 1 +
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 202 ++++++++++++++++---------
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 6 +
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 173 ++++++++++++++++++---
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.h | 10 +-
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 10 --
+ 6 files changed, 299 insertions(+), 103 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index e55edc4..7944631 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -43,6 +43,7 @@ entries
+ 458 4 e 11 hypertransport_speed_limit
+ 462 2 e 12 minimum_memory_voltage
+ 464 1 e 2 compute_unit_siblings
++465 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 771d743..779d9ad 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -39,7 +39,8 @@
+ static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA);
++ struct DCTStatStruc *pDCTstatA,
++ uint8_t allow_config_restore);
+ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+@@ -355,8 +356,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs
+ else if (drive_strength == 0x3)
+ calibration_code = 0xfff;
+ }
+- }
+- else if (ddr_voltage_index & 0x2) {
++ } else if (ddr_voltage_index & 0x2) {
+ /* 1.35V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 42 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -390,8 +390,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs
+ else if (drive_strength == 0x3)
+ calibration_code = 0xdb6;
+ }
+- }
+- else if (ddr_voltage_index & 0x1) {
++ } else if (ddr_voltage_index & 0x1) {
+ /* 1.5V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 41 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -426,8 +425,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs
+ calibration_code = 0xb6d;
+ }
+ }
+- }
+- else if (package_type == PT_C3) {
++ } else if (package_type == PT_C3) {
+ /* Socket C32 */
+ if (ddr_voltage_index & 0x4) {
+ /* 1.25V */
+@@ -473,8 +471,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs
+ else if (drive_strength == 0x3)
+ calibration_code = 0xfff;
+ }
+- }
+- else if (ddr_voltage_index & 0x2) {
++ } else if (ddr_voltage_index & 0x2) {
+ /* 1.35V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 45 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -518,8 +515,7 @@ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTs
+ else if (drive_strength == 0x3)
+ calibration_code = 0xdb6;
+ }
+- }
+- else if (ddr_voltage_index & 0x1) {
++ } else if (ddr_voltage_index & 0x1) {
+ /* 1.5V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 44 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -619,8 +615,7 @@ static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStr
+ else if (drive_strength == 0x3)
+ calibration_code = 0xb64;
+ }
+- }
+- else if (ddr_voltage_index & 0x2) {
++ } else if (ddr_voltage_index & 0x2) {
+ /* 1.35V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 51 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -654,8 +649,7 @@ static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStr
+ else if (drive_strength == 0x3)
+ calibration_code = 0x924;
+ }
+- }
+- else if (ddr_voltage_index & 0x1) {
++ } else if (ddr_voltage_index & 0x1) {
+ /* 1.5V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 50 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -736,8 +730,7 @@ static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *p
+ else if (drive_strength == 0x3)
+ calibration_code = 0xff6;
+ }
+- }
+- else if (ddr_voltage_index & 0x2) {
++ } else if (ddr_voltage_index & 0x2) {
+ /* 1.35V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 54 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -771,8 +764,7 @@ static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *p
+ else if (drive_strength == 0x3)
+ calibration_code = 0xdad;
+ }
+- }
+- else if (ddr_voltage_index & 0x1) {
++ } else if (ddr_voltage_index & 0x1) {
+ /* 1.5V */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 53 */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+@@ -841,16 +833,13 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ else if (MemClkFreq == 0x6) {
+ /* DDR3-800 */
+ calibration_code = 0x10112222;
+- }
+- else if (MemClkFreq == 0xa) {
++ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+ calibration_code = 0x20112222;
+- }
+- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+ /* DDR3-1333 - DDR3-1600 */
+ calibration_code = 0x30112222;
+- }
+- else if (MemClkFreq == 0x16) {
++ } else if (MemClkFreq == 0x16) {
+ /* DDR3-1866 */
+ calibration_code = 0x30332222;
+ }
+@@ -860,16 +849,13 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x00112222;
+- }
+- else if (MemClkFreq == 0x6) {
++ } else if (MemClkFreq == 0x6) {
+ /* DDR3-800 */
+ calibration_code = 0x10112222;
+- }
+- else if (MemClkFreq == 0xa) {
++ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+ calibration_code = 0x20112222;
+- }
+- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+ /* DDR3-1333 - DDR3-1600 */
+ calibration_code = 0x30112222;
+ }
+@@ -881,20 +867,16 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x10222222;
+- }
+- else if (MemClkFreq == 0x6) {
++ } else if (MemClkFreq == 0x6) {
+ /* DDR3-800 */
+ calibration_code = 0x20222222;
+- }
+- else if (MemClkFreq == 0xa) {
++ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+ calibration_code = 0x30222222;
+- }
+- else if (MemClkFreq == 0xe) {
++ } else if (MemClkFreq == 0xe) {
+ /* DDR3-1333 */
+ calibration_code = 0x30222222;
+- }
+- else if (MemClkFreq == 0x12) {
++ } else if (MemClkFreq == 0x12) {
+ /* DDR3-1600 */
+ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
+ calibration_code = 0x30222222;
+@@ -1081,8 +1063,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+ /* DDR3-667 - DDR3-1333 */
+ slow_access = 0;
+- }
+- else if (MemClkFreq == 0x12) {
++ } else if (MemClkFreq == 0x12) {
+ /* DDR3-1600 */
+ if (rank_count_dimm0 == 1)
+ slow_access = 0;
+@@ -1098,8 +1079,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ || (MemClkFreq == 0xa)) {
+ /* DDR3-667 - DDR3-1066 */
+ slow_access = 0;
+- }
+- else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+ /* DDR3-1333 - DDR3-1600 */
+ slow_access = 1;
+ }
+@@ -1184,6 +1164,28 @@ static void read_spd_bytes(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++static void calculate_and_store_spd_hashes(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ uint8_t dimm;
++
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ calculate_spd_hash(pDCTstat->spd_data.spd_bytes[dimm], &pDCTstat->spd_data.spd_hash[dimm]);
++ }
++}
++
++static void compare_nvram_spd_hashes(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ uint8_t dimm;
++
++ pDCTstat->spd_data.nvram_spd_match = 1;
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
++ if (pDCTstat->spd_data.spd_hash[dimm] != pDCTstat->spd_data.nvram_spd_hash[dimm])
++ pDCTstat->spd_data.nvram_spd_match = 0;
++ }
++}
++
+ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1232,6 +1234,8 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ */
+ u8 Node, NodesWmem;
+ u32 node_sys_base;
++ uint8_t nvram;
++ uint8_t allow_config_restore;
+
+ uint8_t s3resume = acpi_is_wakeup_s3();
+
+@@ -1247,7 +1251,7 @@ restartinit:
+ }
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n");
+- restore_mct_information_from_nvram();
++ restore_mct_information_from_nvram(0);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -1296,9 +1300,24 @@ restartinit:
+ node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
+ }
+
++ /* If the boot fails make sure training is attempted after reset */
++ nvram = 0;
++ set_option("allow_spd_nvram_cache_restore", &nvram);
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
+ DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */
+
++ /* If DIMM configuration has not changed since last boot restore training values */
++ allow_config_restore = 1;
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent)
++ if (!pDCTstat->spd_data.nvram_spd_match)
++ allow_config_restore = 0;
++ }
++
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+@@ -1332,14 +1351,33 @@ restartinit:
+ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
+ mctHookAfterCPU(); /* Setup external northbridge(s) */
+
++ /* FIXME
++ * Previous training values should only be used if the current desired
++ * speed is the same as the speed used in the previous boot.
++ * How to get the desired speed at this point in the code?
++ */
++#if 0
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent) {
++ if (pDCTstat->spd_data.nvram_memclk[0] != pDCTstat->DIMMAutoSpeed)
++ allow_config_restore = 0;
++ }
++ }
++#endif
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+- DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/
++ DQSTiming_D(pMCTstat, pDCTstatA, allow_config_restore); /* Get Receiver Enable and DQS signal timing*/
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+- mct_OtherTiming(pMCTstat, pDCTstatA);
++ if (!allow_config_restore) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
++ mct_OtherTiming(pMCTstat, pDCTstatA);
++ }
+
+ if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
+ goto restartinit;
+@@ -1822,7 +1860,7 @@ static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat,
+ }
+
+ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA)
++ struct DCTStatStruc *pDCTstatA, uint8_t allow_config_restore)
+ {
+ u8 nv_DQSTrainCTL;
+
+@@ -1830,9 +1868,8 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ return;
+ }
+
+- nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL);
+- /* FIXME: BOZO- DQS training every time*/
+- nv_DQSTrainCTL = 1;
++ // nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL);
++ nv_DQSTrainCTL = !allow_config_restore;
+
+ mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
+ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
+@@ -1851,15 +1888,16 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++ mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
++ if (!is_fam15h()) {
++ /* TODO: should be in mctHookBeforeAnyTraining */
++ _WRMSR(0x26C, 0x04040404, 0x04040404);
++ _WRMSR(0x26D, 0x04040404, 0x04040404);
++ _WRMSR(0x26E, 0x04040404, 0x04040404);
++ _WRMSR(0x26F, 0x04040404, 0x04040404);
++ }
++
+ if (nv_DQSTrainCTL) {
+- mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
+- if (!is_fam15h()) {
+- /* TODO: should be in mctHookBeforeAnyTraining */
+- _WRMSR(0x26C, 0x04040404, 0x04040404);
+- _WRMSR(0x26D, 0x04040404, 0x04040404);
+- _WRMSR(0x26E, 0x04040404, 0x04040404);
+- _WRMSR(0x26F, 0x04040404, 0x04040404);
+- }
+ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass);
+
+ if (is_fam15h()) {
+@@ -1889,18 +1927,23 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ exit_training_mode_fam15(pMCTstat, pDCTstatA);
+ else
+ mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
++ } else {
++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass);
+
+- /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+- mctHookAfterAnyTraining();
+- mctSaveDQSSigTmg_D();
++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, SecondPass);
+
+- MCTMemClr_D(pMCTstat, pDCTstatA);
+- } else {
+- mctGetDQSSigTmg_D(); /* get values into data structure */
+- LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA); /* load values into registers.*/
+- /* mctDoWarmResetMemClr_D(); */
+- MCTMemClr_D(pMCTstat, pDCTstatA);
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DIMM training configuration from NVRAM\n");
++ restore_mct_information_from_nvram(1);
++
++ if (is_fam15h())
++ exit_training_mode_fam15(pMCTstat, pDCTstatA);
+ }
++
++ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
++ mctHookAfterAnyTraining();
++
++ /* mctDoWarmResetMemClr_D(); */
++ MCTMemClr_D(pMCTstat, pDCTstatA);
+ }
+
+ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+@@ -3910,6 +3953,8 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+ u8 err_code;
++ uint8_t nvram;
++ uint8_t allow_config_restore;
+
+ /* Preconfigure DCT0 */
+ DCTPreInit_D(pMCTstat, pDCTstat, 0);
+@@ -3924,6 +3969,27 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */
+ }
+ }
++
++ calculate_and_store_spd_hashes(pMCTstat, pDCTstat);
++
++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
++ if (load_spd_hashes_from_nvram(pDCTstat) < 0) {
++ pDCTstat->spd_data.nvram_spd_match = 0;
++ }
++ else {
++ compare_nvram_spd_hashes(pMCTstat, pDCTstat);
++ }
++#else
++ pDCTstat->spd_data.nvram_spd_match = 0;
++#endif
++
++ /* Check to see if restoration of SPD data from NVRAM is allowed */
++ allow_config_restore = 0;
++ if (get_option(&nvram, "allow_spd_nvram_cache_restore") == CB_SUCCESS)
++ allow_config_restore = !!nvram;
++
++ if (!allow_config_restore)
++ pDCTstat->spd_data.nvram_spd_match = 0;
+ }
+
+ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 5bb09b4..539ecc3 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -325,6 +325,10 @@ struct MCTStatStruc {
+ struct amd_spd_node_data {
+ uint8_t spd_bytes[MAX_DIMMS_SUPPORTED][256]; /* [DIMM][byte] */
+ uint8_t spd_address[MAX_DIMMS_SUPPORTED]; /* [DIMM] */
++ uint64_t spd_hash[MAX_DIMMS_SUPPORTED]; /* [DIMM] */
++ uint64_t nvram_spd_hash[MAX_DIMMS_SUPPORTED]; /* [DIMM] */
++ uint8_t nvram_spd_match;
++ uint8_t nvram_memclk[2]; /* [channel] */
+ } __attribute__((packed));
+
+ struct DCTStatStruc { /* A per Node structure*/
+@@ -784,6 +788,8 @@ struct amd_s3_persistent_mct_channel_data {
+
+ struct amd_s3_persistent_node_data {
+ uint32_t node_present;
++ uint64_t spd_hash[MAX_DIMMS_SUPPORTED];
++ uint8_t memclk[2];
+ struct amd_s3_persistent_mct_channel_data channel[2];
+ } __attribute__((packed));
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index aa23951..5cdeeb0 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -26,8 +26,10 @@
+ #include <device/pci_ops.h>
+ #include <console/console.h>
+ #include <cbfs.h>
++#include <cbmem.h>
+ #include <spi-generic.h>
+ #include <spi_flash.h>
++#include <pc80/mc146818rtc.h>
+
+ #include "s3utils.h"
+
+@@ -124,6 +126,68 @@ static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint
+ return read_amd_dct_index_register(dev, index_ctl_reg, index);
+ }
+
++/* Non-cryptographic 64-bit hash function taken from Stack Overflow:
++ * http://stackoverflow.com/a/13326345
++ * Any 64-bit hash with sufficiently low collision potential
++ * could be used instead.
++ */
++void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash)
++{
++ const unsigned long long prime = 2654435789ULL;
++ uint16_t byte;
++ *spd_hash = 104395301;
++
++ for (byte = 0; byte < 256; byte++)
++ *spd_hash += (spd_data[byte] * prime) ^ (*spd_hash >> 23);
++
++ *spd_hash = *spd_hash ^ (*spd_hash << 37);
++}
++
++static struct amd_s3_persistent_data * map_s3nv_in_nvram(void)
++{
++ ssize_t s3nv_offset;
++ ssize_t s3nv_file_offset;
++ void * s3nv_cbfs_file_ptr;
++ struct amd_s3_persistent_data *persistent_data;
++
++ /* Obtain CBFS file offset */
++ s3nv_offset = get_s3nv_file_offset();
++ if (s3nv_offset == -1)
++ return NULL;
++
++ /* Align flash pointer to nearest boundary */
++ s3nv_file_offset = s3nv_offset;
++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
++ s3nv_offset += CONFIG_S3_DATA_SIZE;
++ s3nv_file_offset = s3nv_offset - s3nv_file_offset;
++
++ /* Map data structure in CBFS and restore settings */
++ s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL);
++ if (!s3nv_cbfs_file_ptr) {
++ printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME);
++ return NULL;
++ }
++ persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset);
++
++ return persistent_data;
++}
++
++#ifdef __PRE_RAM__
++int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat)
++{
++ struct amd_s3_persistent_data *persistent_data;
++
++ persistent_data = map_s3nv_in_nvram();
++ if (!persistent_data)
++ return -1;
++
++ memcpy(pDCTstat->spd_data.nvram_spd_hash, persistent_data->node[pDCTstat->Node_ID].spd_hash, sizeof(pDCTstat->spd_data.nvram_spd_hash));
++ memcpy(pDCTstat->spd_data.nvram_memclk, persistent_data->node[pDCTstat->Node_ID].memclk, sizeof(pDCTstat->spd_data.nvram_memclk));
++
++ return 0;
++}
++#endif
++
+ #ifdef __RAMSTAGE__
+ static uint64_t rdmsr_uint64_t(unsigned long index) {
+ msr_t msr = rdmsr(index);
+@@ -149,6 +213,31 @@ static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t d
+ return pci_read_config32(dev, reg);
+ }
+
++static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
++{
++ uint8_t node;
++ uint8_t dimm;
++ uint8_t channel;
++ struct amdmct_memory_info *mem_info;
++ mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO);
++ if (mem_info == NULL) {
++ /* can't find amdmct information in cbmem */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++)
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++)
++ persistent_data->node[node].spd_hash[dimm] = 0xffffffffffffffffULL;
++
++ return;
++ }
++
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++)
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++)
++ calculate_spd_hash(mem_info->dct_stat[node].spd_data.spd_bytes[dimm], &persistent_data->node[node].spd_hash[dimm]);
++
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++)
++ for (channel = 0; channel < 2; channel++)
++ persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed;
++}
++
+ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
+ {
+ uint8_t i;
+@@ -441,7 +530,7 @@ static void wrmsr_uint64_t(unsigned long index, uint64_t value) {
+ wrmsr(index, msr);
+ }
+
+-void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data)
++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t training_only)
+ {
+ uint8_t i;
+ uint8_t j;
+@@ -451,6 +540,51 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ uint8_t dct_enabled;
+ uint32_t dword;
+
++ if (training_only) {
++ /* Only restore the Receiver Enable and DQS training registers */
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ for (channel = 0; channel < 2; channel++) {
++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
++ if (!persistent_data->node[node].node_present)
++ continue;
++
++ /* Restore training parameters */
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
++ for (i=0; i<4; i++)
++ for (j=0; j<3; j++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]);
++
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
++
++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
++ for (i=0; i<12; i++)
++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]);
++ }
++
++ /* Restore MaxRdLatency */
++ if (is_fam15h()) {
++ for (i=0; i<4; i++)
++ write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
++ }
++ else {
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
++ }
++
++ /* Other timing control registers */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
++ }
++ }
++
++ return;
++ }
++
+ /* Load data from data structure into DCTs */
+ /* Stage 1 */
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+@@ -501,7 +635,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ wrmsr_uint64_t(0x00000250, data->msr00000250);
+ wrmsr_uint64_t(0x00000258, data->msr00000258);
+ /* FIXME
+- * Restoring these MSRs causes a hang on resume
++ * Restoring these MSRs causes a hang on resume due to
++ * destroying CAR while still executing from CAR!
+ * For now, skip restoration...
+ */
+ // for (i=0; i<8; i++)
+@@ -890,6 +1025,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ #ifdef __RAMSTAGE__
+ int8_t save_mct_information_to_nvram(void)
+ {
++ uint8_t nvram;
++
+ if (acpi_is_wakeup_s3())
+ return 0;
+
+@@ -909,6 +1046,9 @@ int8_t save_mct_information_to_nvram(void)
+ /* Obtain MCT configuration data */
+ copy_mct_data_to_save_variable(persistent_data);
+
++ /* Save RAM SPD data at the same time */
++ copy_cbmem_spd_data_to_save_variable(persistent_data);
++
+ /* Obtain CBFS file offset */
+ s3nv_offset = get_s3nv_file_offset();
+ if (s3nv_offset == -1)
+@@ -949,36 +1089,23 @@ int8_t save_mct_information_to_nvram(void)
+ /* Restore SPI MMIO address */
+ pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev);
+
++ /* Allow training bypass if DIMM configuration is unchanged on next boot */
++ nvram = 1;
++ set_option("allow_spd_nvram_cache_restore", &nvram);
++
+ return 0;
+ }
+ #endif
+
+-int8_t restore_mct_information_from_nvram(void)
++int8_t restore_mct_information_from_nvram(uint8_t training_only)
+ {
+- ssize_t s3nv_offset;
+- ssize_t s3nv_file_offset;
+- void * s3nv_cbfs_file_ptr;
+ struct amd_s3_persistent_data *persistent_data;
+
+- /* Obtain CBFS file offset */
+- s3nv_offset = get_s3nv_file_offset();
+- if (s3nv_offset == -1)
++ persistent_data = map_s3nv_in_nvram();
++ if (!persistent_data)
+ return -1;
+
+- /* Align flash pointer to nearest boundary */
+- s3nv_file_offset = s3nv_offset;
+- s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
+- s3nv_offset += CONFIG_S3_DATA_SIZE;
+- s3nv_file_offset = s3nv_offset - s3nv_file_offset;
+-
+- /* Map data structure in CBFS and restore settings */
+- s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL);
+- if (!s3nv_cbfs_file_ptr) {
+- printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME);
+- return -1;
+- }
+- persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset);
+- restore_mct_data_from_save_variable(persistent_data);
++ restore_mct_data_from_save_variable(persistent_data, training_only);
+
+ return 0;
+ }
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+index dcddcad..82f73a7 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+@@ -20,9 +20,15 @@
+ #include "../wrappers/mcti.h"
+ #include "mct_d.h"
+
++void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash);
++
++#ifdef __PRE_RAM__
++int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat);
++#endif
++
+ #ifdef __RAMSTAGE__
+ int8_t save_mct_information_to_nvram(void);
+ #endif
+-int8_t restore_mct_information_from_nvram(void);
++int8_t restore_mct_information_from_nvram(uint8_t training_only);
+ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data);
+-void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data);
+\ No newline at end of file
++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t training_only);
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index 1d4eade..2e53f0b 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -399,16 +399,6 @@ static void mctHookAfterCPU(void)
+ }
+
+
+-static void mctSaveDQSSigTmg_D(void)
+-{
+-}
+-
+-
+-static void mctGetDQSSigTmg_D(void)
+-{
+-}
+-
+-
+ static void mctHookBeforeECC(void)
+ {
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0046-northbridge-amd-amdfam10-Enable-CC6-DRAM-save-area-s.patch b/resources/libreboot/patch/kgpe-d16/0046-northbridge-amd-amdfam10-Enable-CC6-DRAM-save-area-s.patch
new file mode 100644
index 00000000..7b5b1f8c
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0046-northbridge-amd-amdfam10-Enable-CC6-DRAM-save-area-s.patch
@@ -0,0 +1,259 @@
+From a56e3642d8003813e19d6685c7f8680e1b645b22 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 5 Jun 2015 21:13:30 -0500
+Subject: [PATCH 046/146] northbridge/amd/amdfam10: Enable CC6 DRAM save area
+ setup
+
+---
+ src/northbridge/amd/amdfam10/northbridge.c | 70 ++++++++++++++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 132 +++++++++++++++++++++++++++
+ 2 files changed, 202 insertions(+)
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index a29dad9..d13932c 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -704,6 +704,8 @@ struct chip_operations northbridge_amd_amdfam10_ops = {
+ static void amdfam10_domain_read_resources(device_t dev)
+ {
+ unsigned reg;
++ uint8_t nvram;
++ uint8_t enable_cc6;
+
+ /* Find the already assigned resource pairs */
+ get_fx_devs();
+@@ -747,6 +749,74 @@ static void amdfam10_domain_read_resources(device_t dev)
+ /* Reserve lower DRAM region to force PCI MMIO region to correct location above 0xefffffff */
+ ram_resource(dev, 7, 0, rdmsr(TOP_MEM).lo >> 10);
+ #endif
++
++ if (is_fam15h()) {
++ enable_cc6 = 0;
++ if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
++ enable_cc6 = !!nvram;
++
++ if (enable_cc6) {
++ uint8_t node;
++ uint8_t interleaved;
++ int8_t range;
++ int8_t max_range;
++ uint8_t max_node;
++ uint64_t max_range_limit;
++ uint32_t dword;
++ uint32_t dword2;
++ uint64_t qword;
++ uint8_t num_nodes;
++
++ /* Find highest DRAM range (DramLimitAddr) */
++ max_node = 0;
++ max_range = -1;
++ interleaved = 0;
++ max_range_limit = 0;
++ for (range = 0; range < 8; range++) {
++ dword = f1_read_config32(0x40 + (range * 0x8));
++ if (!(dword & 0x3))
++ continue;
++
++ if ((dword >> 8) & 0x7)
++ interleaved = 1;
++
++ dword = f1_read_config32(0x44 + (range * 0x8));
++ dword2 = f1_read_config32(0x144 + (range * 0x8));
++ qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
++ qword |= (((uint64_t)dword2) & 0xff) << 40;
++
++ if (qword > max_range_limit) {
++ max_range = range;
++ max_range_limit = qword;
++ max_node = dword & 0x7;
++ }
++ }
++
++ num_nodes = 0;
++ device_t node_dev;
++ for (node = 0; node < FX_DEVS; node++) {
++ node_dev = get_node_pci(node, 0);
++ /* Test for node presence */
++ if ((node_dev) && (pci_read_config32(node_dev, PCI_VENDOR_ID) != 0xffffffff))
++ num_nodes++;
++ }
++
++ /* Calculate CC6 sotrage area size */
++ if (interleaved)
++ qword = (0x1000000 * num_nodes);
++ else
++ qword = 0x1000000;
++
++ /* Reserve the CC6 save segment */
++ reserved_ram_resource(dev, 8, max_range_limit >> 10, qword >> 10);
++
++ /* Set up the C-state base address */
++ msr_t c_state_addr_msr;
++ c_state_addr_msr = rdmsr(0xc0010073);
++ c_state_addr_msr.lo = 0xe0e0; /* CstateAddr = 0xe0e0 */
++ wrmsr(0xc0010073, c_state_addr_msr);
++ }
++ }
+ }
+
+ static u32 my_find_pci_tolm(struct bus *bus, u32 tolm)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 779d9ad..0e97715 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1186,6 +1186,100 @@ static void compare_nvram_spd_hashes(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t num_nodes)
++{
++ uint8_t interleaved;
++ uint8_t destination_node;
++ int8_t range;
++ int8_t max_range;
++ uint8_t max_node;
++ uint64_t max_range_limit;
++ uint32_t dword;
++ uint32_t dword2;
++ uint64_t qword;
++
++ interleaved = 0;
++ if (pMCTstat->GStatus & (1 << GSB_NodeIntlv))
++ interleaved = 1;
++
++ /* Find highest DRAM range (DramLimitAddr) */
++ max_node = 0;
++ max_range = -1;
++ max_range_limit = 0;
++ for (range = 0; range < 8; range++) {
++ dword = Get_NB32(pDCTstat->dev_map, 0x40 + (range * 0x8));
++ if (!(dword & 0x3))
++ continue;
++
++ dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
++ dword2 = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
++ qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
++ qword |= (((uint64_t)dword2) & 0xff) << 40;
++
++ if (qword > max_range_limit) {
++ max_range = range;
++ max_range_limit = qword;
++ max_node = dword & 0x7;
++ }
++ }
++
++ if (pDCTstat->Node_ID == max_node) {
++ if (max_range >= 0) {
++ if (interleaved)
++ /* Move upper limit down by 16M * the number of nodes */
++ max_range_limit -= (0x1000000 * num_nodes);
++ else
++ /* Move upper limit down by 16M */
++ max_range_limit -= 0x1000000;
++
++ /* Store modified range */
++ dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
++ dword &= ~(0xffff << 16); /* DramLimit[39:24] = max_range_limit[39:24] */
++ dword |= (max_range_limit >> 24) & 0xffff;
++ Set_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8), dword);
++
++ dword = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
++ dword &= ~(0xffff << 16); /* DramLimit[47:40] = max_range_limit[47:40] */
++ dword |= (max_range_limit >> 40) & 0xff;
++ Set_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8), dword);
++ }
++ }
++
++ /* Determine save state destination node */
++ if (interleaved)
++ destination_node = Get_NB32(pDCTstat->dev_host, 0x60) & 0x7;
++ else
++ destination_node = max_node;
++
++ /* Set save state destination node */
++ dword = Get_NB32(pDCTstat->dev_link, 0x128);
++ dword &= ~(0x3f << 12); /* CoreSaveStateDestNode = destination_node */
++ dword |= (destination_node & 0x3f) << 12;
++ Set_NB32(pDCTstat->dev_link, 0x128, dword);
++}
++
++static void lock_dram_config(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ uint32_t dword;
++
++ dword = Get_NB32(pDCTstat->dev_dct, 0x118);
++ dword |= 0x1 << 19; /* LockDramCfg = 1 */
++ Set_NB32(pDCTstat->dev_dct, 0x118, dword);
++}
++
++static void set_cc6_save_enable(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t enable)
++{
++ uint32_t dword;
++
++ dword = Get_NB32(pDCTstat->dev_dct, 0x118);
++ dword &= ~(0x1 << 18); /* CC6SaveEn = enable */
++ dword |= (enable & 0x1) << 18;
++ Set_NB32(pDCTstat->dev_dct, 0x118, dword);
++}
++
+ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+ {
+@@ -1235,6 +1329,7 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ u8 Node, NodesWmem;
+ u32 node_sys_base;
+ uint8_t nvram;
++ uint8_t enable_cc6;
+ uint8_t allow_config_restore;
+
+ uint8_t s3resume = acpi_is_wakeup_s3();
+@@ -1400,6 +1495,43 @@ restartinit:
+ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
+ }
+
++ if (is_fam15h()) {
++ enable_cc6 = 0;
++ if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
++ enable_cc6 = !!nvram;
++
++ if (enable_cc6) {
++ uint8_t num_nodes;
++
++ num_nodes = 0;
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent)
++ num_nodes++;
++ }
++
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent)
++ set_up_cc6_storage_fam15(pMCTstat, pDCTstat, num_nodes);
++ }
++
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (pDCTstat->NodePresent) {
++ lock_dram_config(pMCTstat, pDCTstat);
++ set_cc6_save_enable(pMCTstat, pDCTstat, 1);
++ }
++ }
++ }
++ }
++
+ mct_FinalMCT_D(pMCTstat, pDCTstatA);
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0047-mainboard-asus-kgpe-d16-Enable-CC6.patch b/resources/libreboot/patch/kgpe-d16/0047-mainboard-asus-kgpe-d16-Enable-CC6.patch
new file mode 100644
index 00000000..aed82458
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0047-mainboard-asus-kgpe-d16-Enable-CC6.patch
@@ -0,0 +1,68 @@
+From baa72eab99ba320f2975d3925d0dc90429cf8919 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 5 Jun 2015 21:14:23 -0500
+Subject: [PATCH 047/146] mainboard/asus/kgpe-d16: Enable CC6
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 3 ++-
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 10 +++++++++-
+ 3 files changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 3e2ea3a..bfd2020 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -14,6 +14,7 @@ ecc_scrub_rate = 1.28us
+ interleave_chip_selects = Enable
+ interleave_nodes = Disable
+ interleave_memory_channels = Enable
++cpu_cc6_state = Enable
+ ieee1394 = Enable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 7944631..630219e 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -43,7 +43,8 @@ entries
+ 458 4 e 11 hypertransport_speed_limit
+ 462 2 e 12 minimum_memory_voltage
+ 464 1 e 2 compute_unit_siblings
+-465 1 r 0 allow_spd_nvram_cache_restore
++465 1 e 1 cpu_cc6_state
++466 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 5cdeeb0..24f78b2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -625,7 +625,11 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124);
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c);
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114);
+- write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
++ if (is_fam15h())
++ /* Do not set LockDramCfg or CC6SaveEn at this time */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118 & ~(0x3 << 18));
++ else
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c);
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0);
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44);
+@@ -1017,6 +1021,10 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+
+ /* ECC scrub rate control */
+ pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58);
++
++ if (is_fam15h())
++ /* Set LockDramCfg and CC6SaveEn */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
+ }
+ }
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0048-cpu-amd-Add-CC6-support.patch b/resources/libreboot/patch/kgpe-d16/0048-cpu-amd-Add-CC6-support.patch
new file mode 100644
index 00000000..df8cc6db
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0048-cpu-amd-Add-CC6-support.patch
@@ -0,0 +1,1302 @@
+From ba75258bcf9104031d7c91c5ced514e0b03f8548 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 8 Jun 2015 19:35:06 -0500
+Subject: [PATCH 048/146] cpu/amd: Add CC6 support
+
+---
+ src/arch/x86/acpigen.c | 26 +++-
+ src/arch/x86/include/arch/acpigen.h | 3 +
+ src/cpu/amd/model_10xxx/fidvid.c | 170 ++++++++++++------------
+ src/cpu/amd/model_10xxx/init_cpus.c | 77 +++++++++++
+ src/cpu/amd/model_10xxx/powernow_acpi.c | 135 +++++++++++++++++--
+ src/include/cpu/amd/powernow.h | 2 +
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 5 +-
+ src/northbridge/amd/amdfam10/link_control.c | 78 +++++++++++
+ src/northbridge/amd/amdfam10/northbridge.c | 58 ++++----
+ src/northbridge/amd/amdht/AsPsDefs.h | 3 +-
+ src/northbridge/amd/amdmct/amddefs.h | 66 ++++-----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 57 ++++----
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 8 ++
+ src/southbridge/amd/sb700/early_setup.c | 20 ++-
+ src/southbridge/amd/sb700/fadt.c | 4 +
+ src/southbridge/amd/sb700/sb700.h | 7 +-
+ src/southbridge/amd/sb700/sm.c | 5 +-
+ src/southbridge/amd/sb800/fadt.c | 3 +
+ src/southbridge/amd/sb800/sb800.h | 8 +-
+ 20 files changed, 536 insertions(+), 200 deletions(-)
+
+diff --git a/src/arch/x86/acpigen.c b/src/arch/x86/acpigen.c
+index 3aa823c..4136e65 100644
+--- a/src/arch/x86/acpigen.c
++++ b/src/arch/x86/acpigen.c
+@@ -1,6 +1,7 @@
+ /*
+ * This file is part of the coreboot project.
+ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -21,11 +22,11 @@
+ #define ACPIGEN_LENSTACK_SIZE 10
+
+ /*
+- * If you need to change this, change acpigen_write_f and
++ * If you need to change this, change acpigen_write_len_f and
+ * acpigen_pop_len
+ */
+
+-#define ACPIGEN_MAXLEN 0xfff
++#define ACPIGEN_MAXLEN 0xfffff
+
+ #include <string.h>
+ #include <arch/acpigen.h>
+@@ -43,6 +44,7 @@ void acpigen_write_len_f(void)
+ len_stack[ltop++] = gencurrent;
+ acpigen_emit_byte(0);
+ acpigen_emit_byte(0);
++ acpigen_emit_byte(0);
+ }
+
+ void acpigen_pop_len(void)
+@@ -52,9 +54,10 @@ void acpigen_pop_len(void)
+ char *p = len_stack[--ltop];
+ len = gencurrent - p;
+ ASSERT(len <= ACPIGEN_MAXLEN)
+- /* generate store length for 0xfff max */
+- p[0] = (0x40 | (len & 0xf));
++ /* generate store length for 0xfffff max */
++ p[0] = (0x80 | (len & 0xf));
+ p[1] = (len >> 4 & 0xff);
++ p[2] = (len >> 12 & 0xff);
+
+ }
+
+@@ -483,6 +486,21 @@ void acpigen_write_CST_package(acpi_cstate_t *cstate, int nentries)
+ acpigen_pop_len();
+ }
+
++void acpigen_write_CSD_package(u32 domain, u32 numprocs, CSD_coord coordtype, u32 index)
++{
++ acpigen_write_name("_CSD");
++ acpigen_write_package(1);
++ acpigen_write_package(6);
++ acpigen_write_byte(6); // 6 values
++ acpigen_write_byte(0); // revision 0
++ acpigen_write_dword(domain);
++ acpigen_write_dword(coordtype);
++ acpigen_write_dword(numprocs);
++ acpigen_write_dword(index);
++ acpigen_pop_len();
++ acpigen_pop_len();
++}
++
+ void acpigen_write_TSS_package(int entries, acpi_tstate_t *tstate_list)
+ {
+ /*
+diff --git a/src/arch/x86/include/arch/acpigen.h b/src/arch/x86/include/arch/acpigen.h
+index a3e65eb..8e50960 100644
+--- a/src/arch/x86/include/arch/acpigen.h
++++ b/src/arch/x86/include/arch/acpigen.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -55,6 +56,8 @@ typedef enum { SW_ALL=0xfc, SW_ANY=0xfd, HW_ALL=0xfe } PSD_coord;
+ void acpigen_write_PSD_package(u32 domain, u32 numprocs, PSD_coord coordtype);
+ void acpigen_write_CST_package_entry(acpi_cstate_t *cstate);
+ void acpigen_write_CST_package(acpi_cstate_t *entry, int nentries);
++typedef enum { CSD_HW_ALL=0xfe } CSD_coord;
++void acpigen_write_CSD_package(u32 domain, u32 numprocs, CSD_coord coordtype, u32 index);
+ void acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len);
+ void acpigen_write_TSS_package(int entries, acpi_tstate_t *tstate_list);
+ void acpigen_write_TSD_package(u32 domain, u32 numprocs, PSD_coord coordtype);
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 2e26645..0e870e3 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -169,87 +169,87 @@ static void applyBoostFIDOffset(device_t dev, uint32_t nodeid) {
+ }
+
+ static void enableNbPState1( device_t dev ) {
+- uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
+- if (cpuRev & AMD_FAM10_C3) {
+- u32 nbPState = (pci_read_config32(dev, 0x1F0) & NB_PSTATE_MASK);
+- if ( nbPState){
+- u32 nbVid1 = (pci_read_config32(dev, 0x1F4) & NB_VID1_MASK) >> NB_VID1_SHIFT;
+- u32 i;
+- for (i = nbPState; i < NM_PS_REG; i++) {
+- msr_t msr = rdmsr(PS_REG_BASE + i);
+- if (msr.hi & PS_EN_MASK ) {
+- msr.hi |= NB_DID_M_ON;
+- msr.lo &= NB_VID_MASK_OFF;
+- msr.lo |= ( nbVid1 << NB_VID_POS);
+- wrmsr(PS_REG_BASE + i, msr);
+- }
+- }
+- }
+- }
++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
++ if (cpuRev & AMD_FAM10_C3) {
++ u32 nbPState = (pci_read_config32(dev, 0x1F0) & NB_PSTATE_MASK);
++ if ( nbPState){
++ u32 nbVid1 = (pci_read_config32(dev, 0x1F4) & NB_VID1_MASK) >> NB_VID1_SHIFT;
++ u32 i;
++ for (i = nbPState; i < NM_PS_REG; i++) {
++ msr_t msr = rdmsr(PS_REG_BASE + i);
++ if (msr.hi & PS_EN_MASK ) {
++ msr.hi |= NB_DID_M_ON;
++ msr.lo &= NB_VID_MASK_OFF;
++ msr.lo |= ( nbVid1 << NB_VID_POS);
++ wrmsr(PS_REG_BASE + i, msr);
++ }
++ }
++ }
++ }
+ }
+
+-static u8 setPStateMaxVal( device_t dev ) {
+- u8 i,maxpstate=0;
+- for (i = 0; i < NM_PS_REG; i++) {
+- msr_t msr = rdmsr(PS_REG_BASE + i);
+- if (msr.hi & PS_IDD_VALUE_MASK) {
+- msr.hi |= PS_EN_MASK ;
+- wrmsr(PS_REG_BASE + i, msr);
+- }
+- if (msr.hi & PS_EN_MASK) {
+- maxpstate = i;
+- }
+- }
+- //FIXME: CPTC2 and HTC_REG should get max per node, not per core ?
+- u32 reg = pci_read_config32(dev, CPTC2);
+- reg &= PS_MAX_VAL_MASK;
+- reg |= (maxpstate << PS_MAX_VAL_POS);
+- pci_write_config32(dev, CPTC2,reg);
+- return maxpstate;
++static u8 setPStateMaxVal(device_t dev) {
++ u8 i, maxpstate=0;
++ for (i = 0; i < NM_PS_REG; i++) {
++ msr_t msr = rdmsr(PS_REG_BASE + i);
++ if (msr.hi & PS_IDD_VALUE_MASK) {
++ msr.hi |= PS_EN_MASK ;
++ wrmsr(PS_REG_BASE + i, msr);
++ }
++ if (msr.hi & PS_EN_MASK) {
++ maxpstate = i;
++ }
++ }
++ //FIXME: CPTC2 and HTC_REG should get max per node, not per core ?
++ u32 reg = pci_read_config32(dev, CPTC2);
++ reg &= PS_MAX_VAL_MASK;
++ reg |= (maxpstate << PS_MAX_VAL_POS);
++ pci_write_config32(dev, CPTC2,reg);
++ return maxpstate;
+ }
+
+ static void dualPlaneOnly( device_t dev ) {
+- // BKDG 2.4.2.7
+-
+- uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
+- if ((mctGetProcessorPackageType() == AMD_PKGTYPE_AM3_2r2)
+- && (cpuRev & AMD_DR_Cx)) { // should be rev C or rev E but there's no constant for E
+- if ( (pci_read_config32(dev, 0x1FC) & DUAL_PLANE_ONLY_MASK)
+- && (pci_read_config32(dev, 0xA0) & PVI_MODE) ){
+- if (cpuid_edx(0x80000007) & CPB_MASK) {
+- // revision E only, but E is apparently not supported yet, therefore untested
+- msr_t minPstate = rdmsr(0xC0010065);
+- wrmsr(0xC0010065, rdmsr(0xC0010068) );
+- wrmsr(0xC0010068,minPstate);
+- } else {
+- msr_t msr;
+- msr.lo=0; msr.hi=0;
+- wrmsr(0xC0010064, rdmsr(0xC0010068) );
+- wrmsr(0xC0010068, msr );
+- }
+-
+- //FIXME: CPTC2 and HTC_REG should get max per node, not per core ?
+- u8 maxpstate = setPStateMaxVal(dev);
+-
+- u32 reg = pci_read_config32(dev, HTC_REG);
+- reg &= HTC_PS_LMT_MASK;
+- reg |= (maxpstate << PS_LIMIT_POS);
+- pci_write_config32(dev, HTC_REG,reg);
+-
+- }
+- }
++ // BKDG 2.4.2.7
++
++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF);
++ if ((mctGetProcessorPackageType() == AMD_PKGTYPE_AM3_2r2)
++ && (cpuRev & (AMD_DR_Cx | AMD_DR_Ex))) {
++ if ((pci_read_config32(dev, 0x1FC) & DUAL_PLANE_ONLY_MASK)
++ && (pci_read_config32(dev, 0xA0) & PVI_MODE)) {
++ if (cpuid_edx(0x80000007) & CPB_MASK) {
++ // revision E only, but E is apparently not supported yet, therefore untested
++ msr_t minPstate = rdmsr(0xC0010065);
++ wrmsr(0xC0010065, rdmsr(0xC0010068));
++ wrmsr(0xC0010068, minPstate);
++ } else {
++ msr_t msr;
++ msr.lo=0; msr.hi=0;
++ wrmsr(0xC0010064, rdmsr(0xC0010068) );
++ wrmsr(0xC0010068, msr);
++ }
++
++ //FIXME: CPTC2 and HTC_REG should get max per node, not per core ?
++ u8 maxpstate = setPStateMaxVal(dev);
++
++ u32 reg = pci_read_config32(dev, HTC_REG);
++ reg &= HTC_PS_LMT_MASK;
++ reg |= (maxpstate << PS_LIMIT_POS);
++ pci_write_config32(dev, HTC_REG,reg);
++ }
++ }
+ }
+
+ static int vidTo100uV(u8 vid)
+-{// returns voltage corresponding to vid in tenths of mV, i.e. hundreds of uV
+- // BKDG #31116 rev 3.48 2.4.1.6
+- int voltage;
+- if (vid >= 0x7c) {
+- voltage = 0;
+- } else {
+- voltage = (15500 - (125*vid));
+- }
+- return voltage;
++{
++ // returns voltage corresponding to vid in tenths of mV, i.e. hundreds of uV
++ // BKDG #31116 rev 3.48 2.4.1.6
++ int voltage;
++ if (vid >= 0x7c) {
++ voltage = 0;
++ } else {
++ voltage = (15500 - (125*vid));
++ }
++ return voltage;
+ }
+
+ static void setVSRamp(device_t dev) {
+@@ -348,7 +348,7 @@ static void recalculateVsSlamTimeSettingOnCorePre(device_t dev)
+ }
+
+ /* Get AltVID */
+- dtemp = pci_read_config32(dev, 0xDC);
++ dtemp = pci_read_config32(dev, 0xdc);
+ bValue = (u8) (dtemp & BIT_MASK_7);
+
+ /* Use the VID with the lowest voltage (higher VID) */
+@@ -512,15 +512,15 @@ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
+ values (min latency) */
+ u32 nbPstate = pci_read_config32(dev,0x1f0) & NB_PSTATE_MASK;
+ u8 nbSynPtrAdj;
+- if ((cpuRev & (AMD_DR_Bx|AMD_DA_Cx) )
+- || ( (cpuRev & AMD_RB_C3) && (nbPstate!=0))) {
+- nbSynPtrAdj = 5;
++ if ((cpuRev & (AMD_DR_Bx | AMD_DA_Cx | AMD_FAM15_ALL) )
++ || ((cpuRev & AMD_RB_C3) && (nbPstate != 0))) {
++ nbSynPtrAdj = 5;
+ } else {
+- nbSynPtrAdj = 6;
++ nbSynPtrAdj = 6;
+ }
+
+- u32 dword = pci_read_config32(dev, 0xDc);
+- dword &= ~ NB_SYN_PTR_ADJ_MASK;
++ u32 dword = pci_read_config32(dev, 0xdc);
++ dword &= ~NB_SYN_PTR_ADJ_MASK;
+ dword |= nbSynPtrAdj << NB_SYN_PTR_ADJ_POS;
+ /* NbsynPtrAdj set to 5 or 6 per BKDG (needs reset) */
+ pci_write_config32(dev, 0xdc, dword);
+@@ -552,7 +552,7 @@ static void config_acpi_pwr_state_ctrl_regs(device_t dev, u32 cpuRev, u8 procPkg
+ }
+ } else { // rev C or later
+ // same doubt as cache scrubbing: ok to check current state ?
+- dword = pci_read_config32(dev, 0xDC);
++ dword = pci_read_config32(dev, 0xdc);
+ u32 cacheFlushOnHalt = dword & (7 << 16);
+ if (!cacheFlushOnHalt) {
+ c1 = 0x80;
+@@ -623,11 +623,11 @@ static void prep_fid_change(void)
+ printk(BIOS_DEBUG, " F3x80: %08x\n", dword);
+ dword = pci_read_config32(dev, 0x84);
+ printk(BIOS_DEBUG, " F3x84: %08x\n", dword);
+- dword = pci_read_config32(dev, 0xD4);
++ dword = pci_read_config32(dev, 0xd4);
+ printk(BIOS_DEBUG, " F3xD4: %08x\n", dword);
+- dword = pci_read_config32(dev, 0xD8);
++ dword = pci_read_config32(dev, 0xd8);
+ printk(BIOS_DEBUG, " F3xD8: %08x\n", dword);
+- dword = pci_read_config32(dev, 0xDC);
++ dword = pci_read_config32(dev, 0xdc);
+ printk(BIOS_DEBUG, " F3xDC: %08x\n", dword);
+ }
+ }
+@@ -756,7 +756,7 @@ static void fixPsNbVidBeforeWR(u32 newNbVid, u32 coreid, u32 dev, u8 pviMode)
+ * synchronization between cores and we don't think
+ * PstatMaxVal is going to be 0 on cold reset anyway ?
+ */
+- if ( ! (pci_read_config32(dev, 0xDC) & (~ PS_MAX_VAL_MASK)) ) {
++ if (!(pci_read_config32(dev, 0xdc) & (~PS_MAX_VAL_MASK))) {
+ printk(BIOS_ERR,"F3xDC[PstateMaxVal] is zero. Northbridge voltage setting will fail. fixPsNbVidBeforeWR in fidvid.c needs fixing. See AMD # 31116 rev 3.48 BKDG 2.4.2.9.1 \n");
+ };
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index aced850..57b9b89 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -30,6 +30,14 @@
+ #include <northbridge/amd/amdfam10/raminit_amdmct.c>
+ #include <reset.h>
+
++#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB700)
++#include <southbridge/amd/sb700/sb700.h>
++#endif
++
++#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_SB800)
++#include <southbridge/amd/sb800/sb800.h>
++#endif
++
+ #if IS_ENABLED(CONFIG_SET_FIDVID)
+ static void prep_fid_change(void);
+ static void init_fidvid_stage2(u32 apicid, u32 nodeid);
+@@ -874,6 +882,8 @@ void cpuSetAMDMSR(uint8_t node_id)
+ u8 i;
+ u32 platform;
+ uint64_t revision;
++ uint8_t nvram;
++ uint8_t enable_c_states;
+
+ printk(BIOS_DEBUG, "cpuSetAMDMSR ");
+
+@@ -936,6 +946,39 @@ void cpuSetAMDMSR(uint8_t node_id)
+ wrmsr(FP_CFG, msr);
+ }
+
++ if (revision & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
++ /* Set up message triggered C1E */
++ msr = rdmsr(0xc0010055);
++ msr.lo &= ~0xffff; /* IOMsgAddr = ACPI_PM_EVT_BLK */
++ msr.lo |= ACPI_PM_EVT_BLK & 0xffff;
++ msr.lo |= (0x1 << 29); /* BmStsClrOnHltEn = 1 */
++ if (revision & AMD_DR_GT_D0) {
++ msr.lo &= ~(0x1 << 28); /* C1eOnCmpHalt = 0 */
++ msr.lo &= ~(0x1 << 27); /* SmiOnCmpHalt = 0 */
++ }
++ wrmsr(0xc0010055, msr);
++
++ msr = rdmsr(0xc0010015);
++ msr.lo |= (0x1 << 12); /* HltXSpCycEn = 1 */
++ wrmsr(0xc0010015, msr);
++ }
++
++ if (revision & (AMD_DR_Ex | AMD_FAM15_ALL)) {
++ enable_c_states = 0;
++#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
++ if (get_option(&nvram, "cpu_c_states") == CB_SUCCESS)
++ enable_c_states = !!nvram;
++#endif
++
++ if (enable_c_states) {
++ /* Set up the C-state base address */
++ msr_t c_state_addr_msr;
++ c_state_addr_msr = rdmsr(0xc0010073);
++ c_state_addr_msr.lo = ACPI_CPU_P_LVL2; /* CstateAddr = ACPI_CPU_P_LVL2 */
++ wrmsr(0xc0010073, c_state_addr_msr);
++ }
++ }
++
+ printk(BIOS_DEBUG, " done\n");
+ }
+
+@@ -950,6 +993,7 @@ static void cpuSetAMDPCI(u8 node)
+ u32 platform;
+ u32 val;
+ u8 offset;
++ uint32_t dword;
+ uint64_t revision;
+
+ printk(BIOS_DEBUG, "cpuSetAMDPCI %02d", node);
+@@ -1008,6 +1052,39 @@ static void cpuSetAMDPCI(u8 node)
+ if (revision & (AMD_DR_B2 | AMD_DR_B3))
+ dctPhyDiag(); */
+
++ if (revision & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
++ /* Set up message triggered C1E */
++ dword = pci_read_config32(NODE_PCI(node, 3), 0xd4);
++ dword &= ~(0x1 << 14); /* CacheFlushImmOnAllHalt = !is_fam15h() */
++ dword |= (is_fam15h()?0:1) << 14;
++ pci_write_config32(NODE_PCI(node, 3), 0xd4, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0xdc);
++ dword |= 0x1 << 26; /* IgnCpuPrbEn = 1 */
++ dword &= ~(0x7f << 19); /* CacheFlushOnHaltTmr = 0x28 */
++ dword |= 0x28 << 19;
++ dword |= 0x7 << 16; /* CacheFlushOnHaltCtl = 0x7 */
++ pci_write_config32(NODE_PCI(node, 3), 0xdc, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0xa0);
++ dword |= 0x1 << 10; /* IdleExitEn = 1 */
++ pci_write_config32(NODE_PCI(node, 3), 0xa0, dword);
++
++ if (revision & AMD_DR_GT_D0) {
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x188);
++ dword |= 0x1 << 4; /* EnStpGntOnFlushMaskWakeup = 1 */
++ pci_write_config32(NODE_PCI(node, 3), 0x188, dword);
++ } else {
++ dword = pci_read_config32(NODE_PCI(node, 4), 0x128);
++ dword &= ~(0x1 << 31); /* CstateMsgDis = 0 */
++ pci_write_config32(NODE_PCI(node, 4), 0x128, dword);
++ }
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0xd4);
++ dword |= 0x1 << 13; /* MTC1eEn = 1 */
++ pci_write_config32(NODE_PCI(node, 3), 0xd4, dword);
++ }
++
+ printk(BIOS_DEBUG, " done\n");
+ }
+
+diff --git a/src/cpu/amd/model_10xxx/powernow_acpi.c b/src/cpu/amd/model_10xxx/powernow_acpi.c
+index 84e5514..8d94d1c 100644
+--- a/src/cpu/amd/model_10xxx/powernow_acpi.c
++++ b/src/cpu/amd/model_10xxx/powernow_acpi.c
+@@ -21,6 +21,7 @@
+
+ #include <console/console.h>
+ #include <stdint.h>
++#include <option.h>
+ #include <cpu/x86/msr.h>
+ #include <arch/acpigen.h>
+ #include <cpu/amd/powernow.h>
+@@ -34,21 +35,29 @@
+ #include <northbridge/amd/amdmct/mct/mct.h>
+ #include <northbridge/amd/amdmct/amddefs.h>
+
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
+ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_power,
+ u32 *pstate_latency, u32 *pstate_control,
+ u32 *pstate_status, int coreID,
+- u32 pcontrol_blk, u8 plen, u8 onlyBSP,
+ uint8_t single_link)
+ {
+ int i;
+ struct cpuid_result cpuid1;
+
+- if ((onlyBSP) && (coreID != 0)) {
+- plen = 0;
+- pcontrol_blk = 0;
+- }
+-
+- acpigen_write_processor(coreID, pcontrol_blk, plen);
+ acpigen_write_empty_PCT();
+ acpigen_write_name("_PSS");
+
+@@ -92,9 +101,62 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p
+ if (cpu)
+ acpigen_write_PSD_package(cpu->path.apic.apic_id, 1, SW_ANY);
+ }
++}
+
+- /* patch the whole Processor token length */
+- acpigen_pop_len();
++static void write_cstates_for_core(int coreID)
++{
++ /* Generate C state entries */
++ uint8_t cstate_count = 1;
++ acpi_cstate_t cstate;
++
++ if (is_fam15h()) {
++ cstate.ctype = 2;
++ cstate.latency = 100;
++ cstate.power = 0;
++ cstate.resource.space_id = ACPI_ADDRESS_SPACE_IO;
++ cstate.resource.bit_width = 8;
++ cstate.resource.bit_offset = 0;
++ cstate.resource.addrl = rdmsr(0xc0010073).lo + 1;
++ cstate.resource.addrh = 0;
++ cstate.resource.resv = 1;
++ } else {
++ cstate.ctype = 2;
++ cstate.latency = 75;
++ cstate.power = 0;
++ cstate.resource.space_id = ACPI_ADDRESS_SPACE_IO;
++ cstate.resource.bit_width = 8;
++ cstate.resource.bit_offset = 0;
++ cstate.resource.addrl = rdmsr(0xc0010073).lo;
++ cstate.resource.addrh = 0;
++ cstate.resource.resv = 1;
++ }
++
++ acpigen_write_CST_package(&cstate, cstate_count);
++
++ /* Find the local APIC ID for the specified core ID */
++ if (is_fam15h()) {
++ struct device* cpu;
++ int cpu_index = 0;
++ for (cpu = all_devices; cpu; cpu = cpu->next) {
++ if ((cpu->path.type != DEVICE_PATH_APIC) ||
++ (cpu->bus->dev->path.type != DEVICE_PATH_CPU_CLUSTER))
++ continue;
++ if (!cpu->enabled)
++ continue;
++ if (cpu_index == coreID)
++ break;
++ cpu_index++;
++ }
++
++ if (cpu) {
++ /* TODO
++ * Detect dual core status and skip CSD generation if dual core is disabled
++ */
++
++ /* Generate C state dependency entries */
++ acpigen_write_CSD_package((cpu->path.apic.apic_id >> 1) & 0x7f, 2, CSD_HW_ALL, 0);
++ }
++ }
+ }
+
+ /*
+@@ -125,6 +187,15 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ u8 index;
+ msr_t msr;
+
++ uint8_t nvram;
++ uint8_t enable_c_states;
++
++ enable_c_states = 0;
++#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
++ if (get_option(&nvram, "cpu_c_states") == CB_SUCCESS)
++ enable_c_states = !!nvram;
++#endif
++
+ /* Get the Processor Brand String using cpuid(0x8000000x) command x=2,3,4 */
+ cpuid1 = cpuid(0x80000002);
+ v = (u32 *) processor_brand;
+@@ -200,6 +271,10 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ return;
+ }
+
++ if (fam15h)
++ /* Set P_LVL2 P_BLK entry */
++ *(((uint8_t *)pcontrol_blk) + 0x04) = (rdmsr(0xc0010073).lo + 1) & 0xff;
++
+ uint8_t pviModeFlag;
+ uint8_t Pstate_max;
+ uint8_t cpufid;
+@@ -318,18 +393,56 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
+ Pstate_latency[index]);
+ }
+
++ /* Enter processor block scope */
+ char pscope[] = "\\_PR";
+-
+ acpigen_write_scope(pscope);
++
+ for (index = 0; index < total_core_count; index++) {
+ /* Determine if this is a single-link processor */
+ node_index = 0x18 + (index / cores_per_node);
+ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(node_index, 0)), 0x80);
+ single_link = !!(((dtemp & 0xff00) >> 8) == 0);
+
++ /* Enter processor core scope */
++ uint8_t plen_cur = plen;
++ uint32_t pcontrol_blk_cur = pcontrol_blk;
++ if ((onlyBSP) && (index != 0)) {
++ plen_cur = 0;
++ pcontrol_blk_cur = 0;
++ }
++ acpigen_write_processor(index, pcontrol_blk_cur, plen_cur);
++
++ /* Write P-state status and dependency objects */
+ write_pstates_for_core(Pstate_num, Pstate_feq, Pstate_power,
+ Pstate_latency, Pstate_control, Pstate_status,
+- index, pcontrol_blk, plen, onlyBSP, single_link);
++ index, single_link);
++
++ /* Write C-state status and dependency objects */
++ if (fam15h && enable_c_states)
++ write_cstates_for_core(index);
++
++ /* Exit processor core scope */
++ acpigen_pop_len();
+ }
++
++ /* Exit processor block scope */
+ acpigen_pop_len();
+ }
++
++void amd_powernow_update_fadt(acpi_fadt_t * fadt)
++{
++ if (is_fam15h()) {
++ fadt->p_lvl2_lat = 101; /* NOTE: While the BKDG states this should
++ * be set to 100, there is no way to meet
++ * the other FADT requirements. I suspect
++ * there is an error in the BKDG for ACPI
++ * 1.x support; disable all FADT-based C
++ * states > 2... */
++ fadt->p_lvl3_lat = 1001;
++ fadt->flags |= 0x1 << 2; /* FLAGS.PROC_C1 = 1 */
++ fadt->flags |= 0x1 << 3; /* FLAGS.P_LVL2_UP = 1 */
++ } else {
++ fadt->cst_cnt = 0;
++ }
++ fadt->pstate_cnt = 0;
++}
+diff --git a/src/include/cpu/amd/powernow.h b/src/include/cpu/amd/powernow.h
+index 85356bd..07817d9 100644
+--- a/src/include/cpu/amd/powernow.h
++++ b/src/include/cpu/amd/powernow.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -21,5 +22,6 @@
+ #define POWERNOW_H
+
+ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP);
++void amd_powernow_update_fadt(acpi_fadt_t * fadt);
+
+ #endif
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index bfd2020..e3eb4fe 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -14,6 +14,7 @@ ecc_scrub_rate = 1.28us
+ interleave_chip_selects = Enable
+ interleave_nodes = Disable
+ interleave_memory_channels = Enable
++cpu_c_states = Enable
+ cpu_cc6_state = Enable
+ ieee1394 = Enable
+ power_on_after_fail = On
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 630219e..7f9f661 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -43,8 +43,9 @@ entries
+ 458 4 e 11 hypertransport_speed_limit
+ 462 2 e 12 minimum_memory_voltage
+ 464 1 e 2 compute_unit_siblings
+-465 1 e 1 cpu_cc6_state
+-466 1 r 0 allow_spd_nvram_cache_restore
++465 1 e 1 cpu_c_states
++466 1 e 1 cpu_cc6_state
++467 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+diff --git a/src/northbridge/amd/amdfam10/link_control.c b/src/northbridge/amd/amdfam10/link_control.c
+index 1091ef4..4acd66c 100644
+--- a/src/northbridge/amd/amdfam10/link_control.c
++++ b/src/northbridge/amd/amdfam10/link_control.c
+@@ -49,15 +49,93 @@ static inline uint8_t is_fam15h(void)
+
+ static void nb_control_init(struct device *dev)
+ {
++ uint8_t nvram;
++ uint8_t enable_c_states;
++ uint8_t enable_cc6;
+ uint32_t dword;
+
+ printk(BIOS_DEBUG, "NB: Function 4 Link Control.. ");
+
++ /* Configure L3 Power Control */
++ dword = pci_read_config32(dev, 0x1c4);
++ dword |= (0x1 << 8); /* L3PwrSavEn = 1 */
++ pci_write_config32(dev, 0x1c4, dword);
++
+ if (is_fam15h()) {
++ /* Configure L3 Control 2 */
++ dword = pci_read_config32(dev, 0x1cc);
++ dword &= ~(0x7 << 6); /* ImplRdProjDelayThresh = 0x2 */
++ dword |= (0x2 << 6);
++ pci_write_config32(dev, 0x1cc, dword);
++
++ /* Configure TDP Accumulator Divisor Control */
++ dword = pci_read_config32(dev, 0x104);
++ dword &= ~(0xfff << 2); /* TdpAccDivRate = 0xc8 */
++ dword |= (0xc8 << 2);
++ dword &= ~0x3; /* TdpAccDivVal = 0x1 */
++ dword |= 0x1;
++ pci_write_config32(dev, 0x104, dword);
++
++ /* Configure Sample and Residency Timers */
++ dword = pci_read_config32(dev, 0x110);
++ dword &= ~0xfff; /* CSampleTimer = 0x1 */
++ dword |= 0x1;
++ pci_write_config32(dev, 0x110, dword);
++
++ /* Configure APM TDP Control */
++ dword = pci_read_config32(dev, 0x16c);
++ dword |= (0x1 << 4); /* ApmTdpLimitIntEn = 1 */
++ pci_write_config32(dev, 0x16c, dword);
++
+ /* Enable APM */
+ dword = pci_read_config32(dev, 0x15c);
+ dword |= (0x1 << 7); /* ApmMasterEn = 1 */
+ pci_write_config32(dev, 0x15c, dword);
++
++ enable_c_states = 0;
++ enable_cc6 = 0;
++#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
++ if (get_option(&nvram, "cpu_c_states") == CB_SUCCESS)
++ enable_c_states = !!nvram;
++
++ if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
++ enable_cc6 = !!nvram;
++#endif
++
++ if (enable_c_states) {
++ /* Configure C-state Control 1 */
++ dword = pci_read_config32(dev, 0x118);
++ dword |= (0x1 << 24); /* PwrGateEnCstAct1 = 1 */
++ dword &= ~(0x7 << 21); /* ClkDivisorCstAct1 = 0x0 */
++ dword &= ~(0x3 << 18); /* CacheFlushTmrSelCstAct1 = 0x1 */
++ dword |= (0x1 << 18);
++ dword |= (0x1 << 17); /* CacheFlushEnCstAct1 = 1 */
++ dword |= (0x1 << 16); /* CpuPrbEnCstAct1 = 1 */
++ dword &= ~(0x1 << 8); /* PwrGateEnCstAct0 = 0 */
++ dword &= ~(0x7 << 5); /* ClkDivisorCstAct0 = 0x0 */
++ dword &= ~(0x3 << 2); /* CacheFlushTmrSelCstAct0 = 0x2 */
++ dword |= (0x2 << 2);
++ dword |= (0x1 << 1); /* CacheFlushEnCstAct0 = 1 */
++ dword |= 0x1; /* CpuPrbEnCstAct0 = 1 */
++ pci_write_config32(dev, 0x118, dword);
++
++ /* Configure C-state Control 2 */
++ dword = pci_read_config32(dev, 0x11c);
++ dword &= ~(0x1 << 8); /* PwrGateEnCstAct2 = 0 */
++ dword &= ~(0x7 << 5); /* ClkDivisorCstAct2 = 0x0 */
++ dword &= ~(0x3 << 2); /* CacheFlushTmrSelCstAct0 = 0x0 */
++ dword &= ~(0x1 << 1); /* CacheFlushEnCstAct0 = 0 */
++ dword &= ~(0x1); /* CpuPrbEnCstAct0 = 0 */
++ pci_write_config32(dev, 0x11c, dword);
++
++ /* Configure C-state Policy Control 1 */
++ dword = pci_read_config32(dev, 0x128);
++ dword &= ~(0x7f << 5); /* CacheFlushTmr = 0x28 */
++ dword |= (0x28 << 5);
++ dword &= ~0x1; /* CoreCstateMode = !enable_cc6 */
++ dword |= ((enable_cc6)?0:1);
++ pci_write_config32(dev, 0x128, dword);
++ }
+ }
+
+ printk(BIOS_DEBUG, "done.\n");
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index d13932c..f777d02 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -768,53 +768,49 @@ static void amdfam10_domain_read_resources(device_t dev)
+ uint8_t num_nodes;
+
+ /* Find highest DRAM range (DramLimitAddr) */
++ num_nodes = 0;
+ max_node = 0;
+ max_range = -1;
+ interleaved = 0;
+ max_range_limit = 0;
+- for (range = 0; range < 8; range++) {
+- dword = f1_read_config32(0x40 + (range * 0x8));
+- if (!(dword & 0x3))
+- continue;
+-
+- if ((dword >> 8) & 0x7)
+- interleaved = 1;
+-
+- dword = f1_read_config32(0x44 + (range * 0x8));
+- dword2 = f1_read_config32(0x144 + (range * 0x8));
+- qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
+- qword |= (((uint64_t)dword2) & 0xff) << 40;
+-
+- if (qword > max_range_limit) {
+- max_range = range;
+- max_range_limit = qword;
+- max_node = dword & 0x7;
+- }
+- }
+-
+- num_nodes = 0;
+ device_t node_dev;
+ for (node = 0; node < FX_DEVS; node++) {
+ node_dev = get_node_pci(node, 0);
+ /* Test for node presence */
+- if ((node_dev) && (pci_read_config32(node_dev, PCI_VENDOR_ID) != 0xffffffff))
+- num_nodes++;
++ if ((!node_dev) || (pci_read_config32(node_dev, PCI_VENDOR_ID) == 0xffffffff))
++ continue;
++
++ num_nodes++;
++ for (range = 0; range < 8; range++) {
++ dword = pci_read_config32(get_node_pci(node, 1), 0x40 + (range * 0x8));
++ if (!(dword & 0x3))
++ continue;
++
++ if ((dword >> 8) & 0x7)
++ interleaved = 1;
++
++ dword = pci_read_config32(get_node_pci(node, 1), 0x44 + (range * 0x8));
++ dword2 = pci_read_config32(get_node_pci(node, 1), 0x144 + (range * 0x8));
++ qword = 0xffffff;
++ qword |= ((((uint64_t)dword) >> 16) & 0xffff) << 24;
++ qword |= (((uint64_t)dword2) & 0xff) << 40;
++
++ if (qword > max_range_limit) {
++ max_range = range;
++ max_range_limit = qword;
++ max_node = dword & 0x7;
++ }
++ }
+ }
+
+- /* Calculate CC6 sotrage area size */
++ /* Calculate CC6 storage area size */
+ if (interleaved)
+ qword = (0x1000000 * num_nodes);
+ else
+ qword = 0x1000000;
+
+ /* Reserve the CC6 save segment */
+- reserved_ram_resource(dev, 8, max_range_limit >> 10, qword >> 10);
+-
+- /* Set up the C-state base address */
+- msr_t c_state_addr_msr;
+- c_state_addr_msr = rdmsr(0xc0010073);
+- c_state_addr_msr.lo = 0xe0e0; /* CstateAddr = 0xe0e0 */
+- wrmsr(0xc0010073, c_state_addr_msr);
++ reserved_ram_resource(dev, 8, (max_range_limit + 1) >> 10, qword >> 10);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdht/AsPsDefs.h b/src/northbridge/amd/amdht/AsPsDefs.h
+index caeb9b4..7f29dd1 100644
+--- a/src/northbridge/amd/amdht/AsPsDefs.h
++++ b/src/northbridge/amd/amdht/AsPsDefs.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -254,7 +255,7 @@
+ #define DUAL_PLANE_NB_VID_SHIFT 17/* for CPU rev <= C */
+
+
+-#define NM_PS_REG 5 /* number of P-state MSR registers */
++#define NM_PS_REG (is_fam15h()?8:5) /* number of P-state MSR registers */
+
+ /* sFidVidInit.outFlags defines */
+ #define PWR_CK_OK 0 /* System board check OK */
+diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h
+index 20a77d3..7aa4698 100644
+--- a/src/northbridge/amd/amdmct/amddefs.h
++++ b/src/northbridge/amd/amdmct/amddefs.h
+@@ -53,32 +53,34 @@
+ /*
+ * Groups - Create as many as you wish, from the above public values
+ */
+-#define AMD_NPT_F2 (AMD_NPT_F2C | AMD_NPT_F2D | AMD_NPT_F2E | AMD_NPT_F2G | AMD_NPT_F2J | AMD_NPT_F2K)
+-#define AMD_NPT_F3 (AMD_NPT_F3L)
+-#define AMD_NPT_Fx (AMD_NPT_F0 | AMD_NPT_F1 | AMD_NPT_F2 | AMD_NPT_F3)
+-#define AMD_NPT_Gx (AMD_NPT_G0A | AMD_NPT_G1B)
+-#define AMD_NPT_ALL (AMD_NPT_Fx | AMD_NPT_Gx)
+-#define AMD_FINEDELAY (AMD_NPT_F0 | AMD_NPT_F1 | AMD_NPT_F2)
+-#define AMD_GT_F0 (AMD_NPT_ALL AND NOT AMD_NPT_F0)
+-#define AMD_DR_Ax (AMD_DR_A0A + AMD_DR_A1B + AMD_DR_A2)
+-#define AMD_DR_Bx (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_B2 | AMD_DR_B3 | AMD_DR_BA)
+-#define AMD_DR_LT_B2 (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_BA)
+-#define AMD_DR_LT_B3 (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_B2 | AMD_DR_BA)
+-#define AMD_DR_GT_B0 (AMD_DR_ALL & ~(AMD_DR_B0))
+-#define AMD_DR_GT_Bx (AMD_DR_ALL & ~(AMD_DR_Ax | AMD_DR_Bx))
+-#define AMD_DR_ALL (AMD_DR_Bx)
+-#define AMD_FAM10_ALL (AMD_DR_ALL | AMD_RB_C2 | AMD_HY_D0 | AMD_DA_C3 | AMD_DA_C2 | AMD_RB_C3 | AMD_HY_D1 | AMD_PH_E0)
+-#define AMD_FAM10_LT_D (AMD_FAM10_ALL & ~(AMD_HY_D0))
+-#define AMD_FAM10_GT_B0 (AMD_FAM10_ALL & ~(AMD_DR_B0))
+-#define AMD_FAM10_REV_D (AMD_HY_D0 | AMD_HY_D1)
+-#define AMD_DA_Cx (AMD_DA_C2 | AMD_DA_C3)
+-#define AMD_DR_Cx (AMD_RB_C2 | AMD_RB_C3 | AMD_DA_Cx)
+-#define AMD_FAM10_C3 (AMD_RB_C3 | AMD_DA_C3)
+-#define AMD_DR_Dx (AMD_HY_D0 | AMD_HY_D1)
+-#define AMD_DRBH_Cx (AMD_DR_Cx | AMD_HY_D0 )
+-#define AMD_DRBA23_RBC2 (AMD_DR_BA | AMD_DR_B2 | AMD_DR_B3 | AMD_RB_C2 )
++#define AMD_NPT_F2 (AMD_NPT_F2C | AMD_NPT_F2D | AMD_NPT_F2E | AMD_NPT_F2G | AMD_NPT_F2J | AMD_NPT_F2K)
++#define AMD_NPT_F3 (AMD_NPT_F3L)
++#define AMD_NPT_Fx (AMD_NPT_F0 | AMD_NPT_F1 | AMD_NPT_F2 | AMD_NPT_F3)
++#define AMD_NPT_Gx (AMD_NPT_G0A | AMD_NPT_G1B)
++#define AMD_NPT_ALL (AMD_NPT_Fx | AMD_NPT_Gx)
++#define AMD_FINEDELAY (AMD_NPT_F0 | AMD_NPT_F1 | AMD_NPT_F2)
++#define AMD_GT_F0 (AMD_NPT_ALL AND NOT AMD_NPT_F0)
++#define AMD_DR_Ax (AMD_DR_A0A + AMD_DR_A1B + AMD_DR_A2)
++#define AMD_DR_Bx (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_B2 | AMD_DR_B3 | AMD_DR_BA)
++#define AMD_DR_Cx (AMD_RB_C2 | AMD_RB_C3 | AMD_DA_Cx)
++#define AMD_DR_Dx (AMD_HY_D0 | AMD_HY_D1)
++#define AMD_DR_Ex (AMD_PH_E0)
++#define AMD_DR_LT_B2 (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_BA)
++#define AMD_DR_LT_B3 (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_B2 | AMD_DR_BA)
++#define AMD_DR_GT_B0 (AMD_DR_ALL & ~(AMD_DR_B0))
++#define AMD_DR_GT_Bx (AMD_DR_ALL & ~(AMD_DR_Ax | AMD_DR_Bx))
++#define AMD_DR_GT_D0 ((AMD_DR_Dx & ~(AMD_HY_D0)) | AMD_DR_Ex)
++#define AMD_DR_ALL (AMD_DR_Bx)
++#define AMD_FAM10_ALL (AMD_DR_ALL | AMD_RB_C2 | AMD_HY_D0 | AMD_DA_C3 | AMD_DA_C2 | AMD_RB_C3 | AMD_HY_D1 | AMD_PH_E0)
++#define AMD_FAM10_LT_D (AMD_FAM10_ALL & ~(AMD_HY_D0))
++#define AMD_FAM10_GT_B0 (AMD_FAM10_ALL & ~(AMD_DR_B0))
++#define AMD_FAM10_REV_D (AMD_HY_D0 | AMD_HY_D1)
++#define AMD_DA_Cx (AMD_DA_C2 | AMD_DA_C3)
++#define AMD_FAM10_C3 (AMD_RB_C3 | AMD_DA_C3)
++#define AMD_DRBH_Cx (AMD_DR_Cx | AMD_HY_D0 )
++#define AMD_DRBA23_RBC2 (AMD_DR_BA | AMD_DR_B2 | AMD_DR_B3 | AMD_RB_C2 )
+ #define AMD_DR_DAC2_OR_C3 (AMD_DA_C2 | AMD_DA_C3 | AMD_RB_C3)
+-#define AMD_FAM15_ALL (AMD_OR_B2 | AMD_OR_C0)
++#define AMD_FAM15_ALL (AMD_OR_B2 | AMD_OR_C0)
+
+ /*
+ * Public Platforms - USE THESE VERSIONS TO MAKE COMPARE WITH CPUPLATFORMTYPE RETURN VALUE
+@@ -91,9 +93,9 @@
+ #define AMD_PTYPE_MC 0x020 /* Multi Core (>2) */
+ #define AMD_PTYPE_UMA 0x040 /* UMA required */
+
+- /*
+- * Groups - Create as many as you wish, from the above public values
+- */
++/*
++ * Groups - Create as many as you wish, from the above public values
++ */
+ #define AMD_PTYPE_ALL 0xFFFFFFFF /* A mask for all */
+
+
+@@ -102,11 +104,11 @@
+ */
+ #define HTPHY_LINKTYPE_HT3 0x00000001
+ #define HTPHY_LINKTYPE_HT1 0x00000002
+-#define HTPHY_LINKTYPE_COHERENT 0x00000004
++#define HTPHY_LINKTYPE_COHERENT 0x00000004
+ #define HTPHY_LINKTYPE_NONCOHERENT 0x00000008
+ #define HTPHY_LINKTYPE_CONNECTED (HTPHY_LINKTYPE_COHERENT | HTPHY_LINKTYPE_NONCOHERENT)
+ #define HTPHY_LINKTYPE_GANGED 0x00000010
+-#define HTPHY_LINKTYPE_UNGANGED 0x00000020
++#define HTPHY_LINKTYPE_UNGANGED 0x00000020
+ #define HTPHY_LINKTYPE_ALL 0x7FFFFFFF
+
+
+@@ -114,7 +116,7 @@
+ * CPU HT PHY REGISTERS, FIELDS, AND MASKS
+ */
+ #define HTPHY_OFFSET_MASK 0xE00001FF
+-#define HTPHY_WRITE_CMD 0x40000000
++#define HTPHY_WRITE_CMD 0x40000000
+ #define HTPHY_IS_COMPLETE_MASK 0x80000000
+ #define HTPHY_DIRECT_MAP 0x20000000
+ #define HTPHY_DIRECT_OFFSET_MASK 0xE000FFFF
+@@ -162,4 +164,4 @@
+ #define AMD_PKGTYPE_S1gX 2
+ #define AMD_PKGTYPE_G34 3
+ #define AMD_PKGTYPE_ASB2 4
+-#define AMD_PKGTYPE_C32 5
++#define AMD_PKGTYPE_C32 5
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 0e97715..1442340 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1195,6 +1195,7 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ int8_t max_range;
+ uint8_t max_node;
+ uint64_t max_range_limit;
++ uint8_t byte;
+ uint32_t dword;
+ uint32_t dword2;
+ uint64_t qword;
+@@ -1214,7 +1215,8 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
+ dword2 = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
+- qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
++ qword = 0xffffff;
++ qword |= ((((uint64_t)dword) >> 16) & 0xffff) << 24;
+ qword |= (((uint64_t)dword2) & 0xff) << 40;
+
+ if (qword > max_range_limit) {
+@@ -1224,26 +1226,35 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+- if (pDCTstat->Node_ID == max_node) {
+- if (max_range >= 0) {
+- if (interleaved)
+- /* Move upper limit down by 16M * the number of nodes */
+- max_range_limit -= (0x1000000 * num_nodes);
+- else
+- /* Move upper limit down by 16M */
+- max_range_limit -= 0x1000000;
+-
+- /* Store modified range */
+- dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
+- dword &= ~(0xffff << 16); /* DramLimit[39:24] = max_range_limit[39:24] */
+- dword |= (max_range_limit >> 24) & 0xffff;
+- Set_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8), dword);
+-
+- dword = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
+- dword &= ~(0xffff << 16); /* DramLimit[47:40] = max_range_limit[47:40] */
+- dword |= (max_range_limit >> 40) & 0xff;
+- Set_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8), dword);
+- }
++ if (max_range >= 0) {
++ if (interleaved)
++ /* Move upper limit down by 16M * the number of nodes */
++ max_range_limit -= (0x1000000 * num_nodes);
++ else
++ /* Move upper limit down by 16M */
++ max_range_limit -= 0x1000000;
++
++ /* Disable the range */
++ dword = Get_NB32(pDCTstat->dev_map, 0x40 + (max_range * 0x8));
++ byte = dword & 0x3;
++ dword &= ~(0x3);
++ Set_NB32(pDCTstat->dev_map, 0x40 + (max_range * 0x8), dword);
++
++ /* Store modified range */
++ dword = Get_NB32(pDCTstat->dev_map, 0x44 + (max_range * 0x8));
++ dword &= ~(0xffff << 16); /* DramLimit[39:24] = max_range_limit[39:24] */
++ dword |= ((max_range_limit >> 24) & 0xffff) << 16;
++ Set_NB32(pDCTstat->dev_map, 0x44 + (max_range * 0x8), dword);
++
++ dword = Get_NB32(pDCTstat->dev_map, 0x144 + (max_range * 0x8));
++ dword &= ~0xff; /* DramLimit[47:40] = max_range_limit[47:40] */
++ dword |= (max_range_limit >> 40) & 0xff;
++ Set_NB32(pDCTstat->dev_map, 0x144 + (max_range * 0x8), dword);
++
++ /* Reenable the range */
++ dword = Get_NB32(pDCTstat->dev_map, 0x40 + (max_range * 0x8));
++ dword |= byte;
++ Set_NB32(pDCTstat->dev_map, 0x40 + (max_range * 0x8), dword);
+ }
+
+ /* Determine save state destination node */
+@@ -1525,8 +1536,8 @@ restartinit:
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+- lock_dram_config(pMCTstat, pDCTstat);
+ set_cc6_save_enable(pMCTstat, pDCTstat, 1);
++ lock_dram_config(pMCTstat, pDCTstat);
+ }
+ }
+ }
+@@ -5103,7 +5114,7 @@ static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
+ /* get base/limit from Node0 */
+ reg = 0x40 + (Node << 3); /* Node0/Dram Base 0 */
+ val = Get_NB32(dev, reg);
+- Drambase = val >> ( 16 + 3);
++ Drambase = val >> (16 + 3);
+
+ reg = 0x44 + (Node << 3); /* Node0/Dram Base 0 */
+ val = Get_NB32(dev, reg);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index 968f5e5..c930380 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -159,6 +159,14 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ if (MemClrECC) {
+ MCTMemClrSync_D(pMCTstat, pDCTstatA);
+ }
++
++ if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
++ /* Set up message triggered C1E */
++ val = pci_read_config32(pDCTstat->dev_nbmisc, 0xd4);
++ val &= ~(0x1 << 15); /* StutterScrubEn = DRAM scrub enabled */
++ val |= (mctGet_NVbits(NV_DramBKScrub)?1:0) << 15;
++ pci_write_config32(pDCTstat->dev_nbmisc, 0xd4, val);
++ }
+ } /* if Node present */
+ }
+
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index a6849b0..fd3b099 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -22,6 +22,7 @@
+ #define _SB700_EARLY_SETUP_C_
+
+ #include <stdint.h>
++#include <option.h>
+ #include <arch/acpi.h>
+ #include <arch/cpu.h>
+ #include <arch/io.h>
+@@ -271,10 +272,6 @@ void enable_fid_change_on_sb(u32 sbbusn, u32 sbdn)
+ byte &= ~(1<<6);
+ pmio_write(0x8d, byte);
+
+- byte = pmio_read(0x61);
+- byte &= ~0x04;
+- pmio_write(0x61, byte);
+-
+ byte = pmio_read(0x42);
+ byte &= ~0x04;
+ pmio_write(0x42, byte);
+@@ -560,6 +557,13 @@ static void sb700_devices_por_init(void)
+ static void sb700_pmio_por_init(void)
+ {
+ u8 byte;
++ uint8_t enable_c_states;
++
++ enable_c_states = 0;
++#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
++ if (get_option(&byte, "cpu_c_states") == CB_SUCCESS)
++ enable_c_states = !!byte;
++#endif
+
+ printk(BIOS_INFO, "sb700_pmio_por_init()\n");
+ /* K8KbRstEn, KB_RST# control for K8 system. */
+@@ -621,6 +625,14 @@ static void sb700_pmio_por_init(void)
+ byte |= 1 << 0;
+ pmio_write(0xB2, byte);
+
++ /* Set up IOAPIC and BM_STS monitoring */
++ byte = pmio_read(0x61);
++ if (enable_c_states)
++ byte |= 0x4;
++ else
++ byte &= ~0x04;
++ pmio_write(0x61, byte);
++
+ // FIXME: Enabling this causes boot to hang while initializing processors.
+ // /* Enable automatic C1e state switch */
+ // byte = pmio_read(0xc9);
+diff --git a/src/southbridge/amd/sb700/fadt.c b/src/southbridge/amd/sb700/fadt.c
+index 96996a3..6b1924f 100644
+--- a/src/southbridge/amd/sb700/fadt.c
++++ b/src/southbridge/amd/sb700/fadt.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -26,6 +27,7 @@
+ #include <arch/acpi.h>
+ #include <arch/io.h>
+ #include <device/device.h>
++#include <cpu/amd/powernow.h>
+ #include "sb700.h"
+
+ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+@@ -156,5 +158,7 @@ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+ fadt->x_gpe1_blk.addrl = 0;
+ fadt->x_gpe1_blk.addrh = 0x0;
+
++ amd_powernow_update_fadt(fadt);
++
+ header->checksum = acpi_checksum((void *)fadt, sizeof(acpi_fadt_t));
+ }
+diff --git a/src/southbridge/amd/sb700/sb700.h b/src/southbridge/amd/sb700/sb700.h
+index b477091..941a4fd 100644
+--- a/src/southbridge/amd/sb700/sb700.h
++++ b/src/southbridge/amd/sb700/sb700.h
+@@ -36,10 +36,11 @@
+
+ #define ACPI_PM_EVT_BLK (SB700_ACPI_IO_BASE + 0x00) /* 4 bytes */
+ #define ACPI_PM1_CNT_BLK (SB700_ACPI_IO_BASE + 0x04) /* 2 bytes */
+-#define ACPI_PMA_CNT_BLK (SB700_ACPI_IO_BASE + 0x0E) /* 1 byte */
+-#define ACPI_PM_TMR_BLK (SB700_ACPI_IO_BASE + 0x18) /* 4 bytes */
+-#define ACPI_GPE0_BLK (SB700_ACPI_IO_BASE + 0x10) /* 8 bytes */
++#define ACPI_PMA_CNT_BLK (SB700_ACPI_IO_BASE + 0x16) /* 1 byte */
++#define ACPI_PM_TMR_BLK (SB700_ACPI_IO_BASE + 0x20) /* 4 bytes */
++#define ACPI_GPE0_BLK (SB700_ACPI_IO_BASE + 0x18) /* 8 bytes */
+ #define ACPI_CPU_CONTROL (SB700_ACPI_IO_BASE + 0x08) /* 6 bytes */
++#define ACPI_CPU_P_LVL2 (ACPI_CPU_CONTROL + 0x4) /* 1 byte */
+
+ extern void pm_iowrite(u8 reg, u8 value);
+ extern u8 pm_ioread(u8 reg);
+diff --git a/src/southbridge/amd/sb700/sm.c b/src/southbridge/amd/sb700/sm.c
+index 3e6ddf1..200522a 100644
+--- a/src/southbridge/amd/sb700/sm.c
++++ b/src/southbridge/amd/sb700/sm.c
+@@ -112,7 +112,10 @@ static void sm_init(device_t dev)
+ pci_write_config8(dev, 0x41, byte);
+
+ byte = pm_ioread(0x61);
+- byte |= 1 << 1; /* Set to enable NB/SB handshake during IOAPIC interrupt for AMD K8/K7 */
++ if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX))
++ byte &= ~(1 << 1); /* Clear for non-K8 CPUs */
++ else
++ byte |= 1 << 1; /* Set to enable NB/SB handshake during IOAPIC interrupt for AMD K8/K7 */
+ pm_iowrite(0x61, byte);
+
+ /* disable SMI */
+diff --git a/src/southbridge/amd/sb800/fadt.c b/src/southbridge/amd/sb800/fadt.c
+index fea98f9..5250e20 100644
+--- a/src/southbridge/amd/sb800/fadt.c
++++ b/src/southbridge/amd/sb800/fadt.c
+@@ -26,6 +26,7 @@
+ #include <arch/acpi.h>
+ #include <arch/io.h>
+ #include <device/device.h>
++#include <cpu/amd/powernow.h>
+ #include "sb800.h"
+
+ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+@@ -156,5 +157,7 @@ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+ fadt->x_gpe1_blk.addrl = 0;
+ fadt->x_gpe1_blk.addrh = 0x0;
+
++ amd_powernow_update_fadt(fadt);
++
+ header->checksum = acpi_checksum((void *)fadt, sizeof(acpi_fadt_t));
+ }
+diff --git a/src/southbridge/amd/sb800/sb800.h b/src/southbridge/amd/sb800/sb800.h
+index 9049182..3e3f077 100644
+--- a/src/southbridge/amd/sb800/sb800.h
++++ b/src/southbridge/amd/sb800/sb800.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -35,10 +36,11 @@
+
+ #define ACPI_PM_EVT_BLK (SB800_ACPI_IO_BASE + 0x00) /* 4 bytes */
+ #define ACPI_PM1_CNT_BLK (SB800_ACPI_IO_BASE + 0x04) /* 2 bytes */
+-#define ACPI_PMA_CNT_BLK (SB800_ACPI_IO_BASE + 0x0F) /* 1 byte */
+-#define ACPI_PM_TMR_BLK (SB800_ACPI_IO_BASE + 0x18) /* 4 bytes */
+-#define ACPI_GPE0_BLK (SB800_ACPI_IO_BASE + 0x10) /* 8 bytes */
++#define ACPI_PMA_CNT_BLK (SB800_ACPI_IO_BASE + 0x17) /* 1 byte */
++#define ACPI_PM_TMR_BLK (SB800_ACPI_IO_BASE + 0x20) /* 4 bytes */
++#define ACPI_GPE0_BLK (SB800_ACPI_IO_BASE + 0x18) /* 8 bytes */
+ #define ACPI_CPU_CONTROL (SB800_ACPI_IO_BASE + 0x08) /* 6 bytes */
++#define ACPI_CPU_P_LVL2 (ACPI_CPU_CONTROL + 0x4) /* 1 byte */
+
+ void pm_iowrite(u8 reg, u8 value);
+ u8 pm_ioread(u8 reg);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0049-northbridge-amd-amdmct-Skip-DCT-config-write-to-Flas.patch b/resources/libreboot/patch/kgpe-d16/0049-northbridge-amd-amdmct-Skip-DCT-config-write-to-Flas.patch
new file mode 100644
index 00000000..eff83747
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0049-northbridge-amd-amdmct-Skip-DCT-config-write-to-Flas.patch
@@ -0,0 +1,101 @@
+From 3632ae70a8596e983588296158045552a0fdf33e Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 8 Jun 2015 19:54:56 -0500
+Subject: [PATCH 049/146] northbridge/amd/amdmct: Skip DCT config write to
+ Flash if unchanged
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 3 +++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 1 +
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 21 +++++++++++++++++++--
+ 3 files changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 1442340..6d8c23e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1358,6 +1358,7 @@ restartinit:
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n");
+ restore_mct_information_from_nvram(0);
++ pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -2080,6 +2081,8 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+
+ if (is_fam15h())
+ exit_training_mode_fam15(pMCTstat, pDCTstatA);
++
++ pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
+ }
+
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 539ecc3..adf89b2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -316,6 +316,7 @@ struct MCTStatStruc {
+ #define GSB_SpIntRemapHole 16 /* Special condition for Node Interleave and HW remapping*/
+ #define GSB_EnDIMMSpareNW 17 /* Indicates that DIMM Spare can be used without a warm reset */
+ /* NOTE: This is a local bit used by memory code */
++#define GSB_ConfigRestored 18 /* Training configuration was restored from NVRAM */
+
+ /*===============================================================================
+ Local DCT Status structure (a structure for each DCT)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 24f78b2..4c0e58d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -213,7 +213,7 @@ static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t d
+ return pci_read_config32(dev, reg);
+ }
+
+-static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
++static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t * restored)
+ {
+ uint8_t node;
+ uint8_t dimm;
+@@ -236,6 +236,13 @@ static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data*
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++)
+ for (channel = 0; channel < 2; channel++)
+ persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed;
++
++ if (restored) {
++ if (mem_info->mct_stat.GStatus & (1 << GSB_ConfigRestored))
++ *restored = 1;
++ else
++ *restored = 0;
++ }
+ }
+
+ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
+@@ -1034,6 +1041,7 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ int8_t save_mct_information_to_nvram(void)
+ {
+ uint8_t nvram;
++ uint8_t restored = 0;
+
+ if (acpi_is_wakeup_s3())
+ return 0;
+@@ -1055,7 +1063,16 @@ int8_t save_mct_information_to_nvram(void)
+ copy_mct_data_to_save_variable(persistent_data);
+
+ /* Save RAM SPD data at the same time */
+- copy_cbmem_spd_data_to_save_variable(persistent_data);
++ copy_cbmem_spd_data_to_save_variable(persistent_data, &restored);
++
++ if (restored) {
++ /* Allow training bypass if DIMM configuration is unchanged on next boot */
++ nvram = 1;
++ set_option("allow_spd_nvram_cache_restore", &nvram);
++
++ printk(BIOS_DEBUG, "Hardware configuration unchanged since last boot; skipping write\n");
++ return 0;
++ }
+
+ /* Obtain CBFS file offset */
+ s3nv_offset = get_s3nv_file_offset();
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0050-southbridge-amd-sb700-Add-AHCI-support.patch b/resources/libreboot/patch/kgpe-d16/0050-southbridge-amd-sb700-Add-AHCI-support.patch
new file mode 100644
index 00000000..8fde17cb
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0050-southbridge-amd-sb700-Add-AHCI-support.patch
@@ -0,0 +1,658 @@
+From 2b426e29b8a34e7ce3ae479d6472d52687903165 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 9 Jun 2015 18:09:50 -0500
+Subject: [PATCH 050/146] southbridge/amd/sb700: Add AHCI support
+
+---
+ src/include/device/pci_ids.h | 2 +
+ src/mainboard/asus/kgpe-d16/Kconfig | 4 +
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 3 +-
+ src/southbridge/amd/sb700/Kconfig | 4 +
+ src/southbridge/amd/sb700/early_setup.c | 73 ++++++---
+ src/southbridge/amd/sb700/ide.c | 42 +++--
+ src/southbridge/amd/sb700/sata.c | 264 ++++++++++++++++++++----------
+ src/southbridge/amd/sb800/fadt.c | 1 +
+ 9 files changed, 271 insertions(+), 123 deletions(-)
+
+diff --git a/src/include/device/pci_ids.h b/src/include/device/pci_ids.h
+index fcaf4aa..664ac49 100644
+--- a/src/include/device/pci_ids.h
++++ b/src/include/device/pci_ids.h
+@@ -311,6 +311,8 @@
+
+ #define PCI_DEVICE_ID_ATI_SB700_LPC 0x439D
+ #define PCI_DEVICE_ID_ATI_SB700_SATA 0x4390
++#define PCI_DEVICE_ID_ATI_SB700_SATA_AHCI 0x4391
++#define PCI_DEVICE_ID_ATI_SB700_SATA_AHCI_AMD 0x4394
+ #define PCI_DEVICE_ID_ATI_SB700_IDE 0x439C
+ #define PCI_DEVICE_ID_ATI_SB700_HDA 0x4383
+ #define PCI_DEVICE_ID_ATI_SB700_PCI 0x4384
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index d0af47f..9c359da 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -88,6 +88,10 @@ config IRQ_SLOT_COUNT
+ int
+ default 13
+
++config SOUTHBRIDGE_AMD_SB700_SATA_PORT_COUNT_BITFIELD
++ hex
++ default 0x3f
++
+ config ONBOARD_VGA_IS_PRIMARY
+ bool
+ default y
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index e3eb4fe..5bfaadd 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -16,6 +16,7 @@ interleave_nodes = Disable
+ interleave_memory_channels = Enable
+ cpu_c_states = Enable
+ cpu_cc6_state = Enable
++sata_ahci_mode = Enable
+ ieee1394 = Enable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 7f9f661..247fd7b 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -45,7 +45,8 @@ entries
+ 464 1 e 2 compute_unit_siblings
+ 465 1 e 1 cpu_c_states
+ 466 1 e 1 cpu_cc6_state
+-467 1 r 0 allow_spd_nvram_cache_restore
++467 1 e 1 sata_ahci_mode
++468 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+diff --git a/src/southbridge/amd/sb700/Kconfig b/src/southbridge/amd/sb700/Kconfig
+index 064f32e..246b645 100644
+--- a/src/southbridge/amd/sb700/Kconfig
++++ b/src/southbridge/amd/sb700/Kconfig
+@@ -45,6 +45,10 @@ config SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA
+ bool
+ default n
+
++config SOUTHBRIDGE_AMD_SB700_SATA_PORT_COUNT_BITFIELD
++ hex
++ default 0xf
++
+ config EHCI_BAR
+ hex
+ default 0xfef00000
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index fd3b099..70a2aee 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -354,9 +354,13 @@ static void sb700_devices_por_init(void)
+ {
+ device_t dev;
+ u8 byte;
+-#if CONFIG_SOUTHBRIDGE_AMD_SUBTYPE_SP5100
+- u32 dword;
+-#endif
++ uint32_t dword;
++ uint8_t nvram;
++ uint8_t sata_ahci_mode;
++
++ sata_ahci_mode = 0;
++ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
++ sata_ahci_mode = !!nvram;
+
+ printk(BIOS_INFO, "sb700_devices_por_init()\n");
+ /* SMBus Device, BDF:0-20-0 */
+@@ -517,34 +521,56 @@ static void sb700_devices_por_init(void)
+ /* Enable PCIB_DUAL_EN_UP will fix potential problem with PCI cards. */
+ pci_write_config8(dev, 0x50, 0x01);
+
++ if (!sata_ahci_mode){
+ #if CONFIG_SOUTHBRIDGE_AMD_SUBTYPE_SP5100
+- /* SP5100 default SATA mode is RAID5 MODE */
+- dev = pci_locate_device(PCI_ID(0x1002, 0x4393), 0);
+- /* Set SATA Operation Mode, Set to IDE mode */
+- byte = pci_read_config8(dev, 0x40);
+- byte |= (1 << 0);
+- pci_write_config8(dev, 0x40, byte);
++ /* SP5100 default SATA mode is RAID5 MODE */
++ dev = pci_locate_device(PCI_ID(0x1002, 0x4393), 0);
++
++ /* Set SATA Operation Mode, Set to IDE mode */
++ byte = pci_read_config8(dev, 0x40);
++ byte |= (1 << 0);
++ pci_write_config8(dev, 0x40, byte);
+
+- dword = 0x01018f00;
+- pci_write_config32(dev, 0x8, dword);
++ dword = 0x01018f00;
++ pci_write_config32(dev, 0x8, dword);
+
+- /* set SATA Device ID writable */
+- dword = pci_read_config32(dev, 0x40);
+- dword &= ~(1 << 24);
+- pci_write_config32(dev, 0x40, dword);
++ /* set SATA Device ID writable */
++ dword = pci_read_config32(dev, 0x40);
++ dword &= ~(1 << 24);
++ pci_write_config32(dev, 0x40, dword);
+
+- /* set Device ID accommodate with IDE emulation mode configuration*/
+- pci_write_config32(dev, 0x0, 0x43901002);
++ /* set Device ID consistent with IDE emulation mode configuration */
++ pci_write_config32(dev, 0x0, 0x43901002);
+
+- /* rpr v2.13 4.17 Reset CPU on Sync Flood */
+- abcfg_reg(0x10050, 1 << 2, 1 << 2);
++ /* rpr v2.13 4.17 Reset CPU on Sync Flood */
++ abcfg_reg(0x10050, 1 << 2, 1 << 2);
+ #endif
++ }
+
+ /* SATA Device, BDF:0-17-0, Non-Raid-5 SATA controller */
+- printk(BIOS_INFO, "sb700_devices_por_init(): SATA Device, BDF:0-18-0\n");
++ printk(BIOS_INFO, "sb700_devices_por_init(): SATA Device, BDF:0-17-0\n");
+ dev = pci_locate_device(PCI_ID(0x1002, 0x4390), 0);
+
+- /*PHY Global Control*/
++ if (sata_ahci_mode) {
++ /* Switch to AHCI mode (AMD inbox) */
++ dword = pci_read_config32(dev, 0x40);
++ dword |= (0x1 << 24); /* Lock Flash Device ID = 1 */
++ pci_write_config32(dev, 0x40, dword);
++
++ /* Deactivate Sub-Class Code write protection */
++ byte = pci_read_config8(dev, 0x40);
++ byte |= (1 << 0);
++ pci_write_config8(dev, 0x40, byte);
++
++ dword = pci_read_config32(dev, 0x08);
++ dword &= ~(0xff << 16); /* Sub-Class Code = 0x6 */
++ dword |= (0x6 << 16);
++ dword &= ~(0xff << 8); /* Operating Mode Selection = 0x1 */
++ dword |= (0x1 << 8);
++ pci_write_config32(dev, 0x08, dword);
++ }
++
++ /* PHY Global Control */
+ pci_write_config16(dev, 0x86, 0x2C00);
+ }
+
+@@ -695,6 +721,11 @@ static void sb700_pci_cfg(void)
+
+ /* SATA Device, BDF:0-17-0, Non-Raid-5 SATA controller */
+ dev = pci_locate_device(PCI_ID(0x1002, 0x4390), 0);
++ if (dev == PCI_DEV_INVALID)
++ dev = pci_locate_device(PCI_ID(0x1002, 0x4391), 0);
++ if (dev == PCI_DEV_INVALID)
++ dev = pci_locate_device(PCI_ID(0x1002, 0x4394), 0);
++
+ /* rpr7.12 SATA MSI and D3 Power State Capability. */
+ byte = pci_read_config8(dev, 0x40);
+ byte |= 1 << 0;
+diff --git a/src/southbridge/amd/sb700/ide.c b/src/southbridge/amd/sb700/ide.c
+index 8803d7f..4e07717 100644
+--- a/src/southbridge/amd/sb700/ide.c
++++ b/src/southbridge/amd/sb700/ide.c
+@@ -1,6 +1,7 @@
+ /*
+ * This file is part of the coreboot project.
+ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -22,6 +23,7 @@
+ #include <device/pci.h>
+ #include <device/pci_ids.h>
+ #include <device/pci_ops.h>
++#include <option.h>
+ #include "sb700.h"
+
+ static void ide_init(struct device *dev)
+@@ -30,6 +32,12 @@ static void ide_init(struct device *dev)
+ /* Enable ide devices so the linux ide driver will work */
+ u32 dword;
+ u8 byte;
++ uint8_t nvram;
++ uint8_t sata_ahci_mode;
++
++ sata_ahci_mode = 0;
++ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
++ sata_ahci_mode = !!nvram;
+
+ conf = dev->chip_info;
+
+@@ -39,25 +47,25 @@ static void ide_init(struct device *dev)
+ dword &= ~(1 << 16);
+ pci_write_config32(dev, 0x70, dword);
+
+- /* Enable UDMA on all devices, it will become UDMA0 (default PIO is PIO0) */
+- byte = pci_read_config8(dev, 0x54);
+- byte |= 0xf;
+- pci_write_config8(dev, 0x54, byte);
+-
+- /* Enable I/O Access&& Bus Master */
+- dword = pci_read_config16(dev, 0x4);
+- dword |= 1 << 2;
+- pci_write_config16(dev, 0x4, dword);
+-
+- /* set ide as primary, if you want to boot from IDE, you'd better set it
+- * in $vendor/$mainboard/devicetree.cb */
++ if (!sata_ahci_mode) {
++ /* Enable UDMA on all devices, it will become UDMA0 (default PIO is PIO0) */
++ byte = pci_read_config8(dev, 0x54);
++ byte |= 0xf;
++ pci_write_config8(dev, 0x54, byte);
+
++ /* Enable I/O Access&& Bus Master */
++ dword = pci_read_config16(dev, 0x4);
++ dword |= 1 << 2;
++ pci_write_config16(dev, 0x4, dword);
+
+- if (conf->boot_switch_sata_ide == 1) {
+- struct device *sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
+- byte = pci_read_config8(sm_dev, 0xAD);
+- byte |= 1 << 4;
+- pci_write_config8(sm_dev, 0xAD, byte);
++ /* set ide as primary, if you want to boot from IDE, you'd better set it
++ * in $vendor/$mainboard/devicetree.cb */
++ if (conf->boot_switch_sata_ide == 1) {
++ struct device *sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
++ byte = pci_read_config8(sm_dev, 0xad);
++ byte |= 1 << 4;
++ pci_write_config8(sm_dev, 0xad, byte);
++ }
+ }
+ }
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index 172ad36..c0b3cd5 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,18 +25,19 @@
+ #include <device/pci_ids.h>
+ #include <device/pci_ops.h>
+ #include <arch/io.h>
++#include <option.h>
+ #include "sb700.h"
+
+-static int sata_drive_detect(int portnum, u16 iobar)
++static int sata_drive_detect(int portnum, uint16_t iobar)
+ {
+ u8 byte, byte2;
+ int i = 0;
+- outb(0xA0 + 0x10 * (portnum % 2), iobar + 0x6);
++ outb(0xa0 + 0x10 * (portnum % 2), iobar + 0x6);
+ while (byte = inb(iobar + 0x6), byte2 = inb(iobar + 0x7),
+- (byte != (0xA0 + 0x10 * (portnum % 2))) ||
++ (byte != (0xa0 + 0x10 * (portnum % 2))) ||
+ ((byte2 & 0x88) != 0)) {
+ printk(BIOS_SPEW, "0x6=%x, 0x7=%x\n", byte, byte2);
+- if (byte != (0xA0 + 0x10 * (portnum % 2))) {
++ if (byte != (0xa0 + 0x10 * (portnum % 2))) {
+ /* This will happen at the first iteration of this loop
+ * if the first SATA port is unpopulated and the
+ * second SATA port is populated.
+@@ -65,15 +67,15 @@ void __attribute__((weak)) sb7xx_51xx_setup_sata_phys(struct device *dev)
+ pci_write_config32(dev, 0x90, 0x01B48016);
+ pci_write_config32(dev, 0x94, 0x01B48016);
+ pci_write_config32(dev, 0x98, 0x01B48016);
+- pci_write_config32(dev, 0x9C, 0x01B48016);
++ pci_write_config32(dev, 0x9c, 0x01B48016);
+
+ /* RPR7.6.3 SATA GEN II PHY port setting for port [0~5]. */
+- pci_write_config16(dev, 0xA0, 0xA09A);
+- pci_write_config16(dev, 0xA2, 0xA09F);
+- pci_write_config16(dev, 0xA4, 0xA07A);
+- pci_write_config16(dev, 0xA6, 0xA07A);
+- pci_write_config16(dev, 0xA8, 0xA07A);
+- pci_write_config16(dev, 0xAA, 0xA07A);
++ pci_write_config16(dev, 0xa0, 0xA09A);
++ pci_write_config16(dev, 0xa2, 0xA09F);
++ pci_write_config16(dev, 0xa4, 0xA07A);
++ pci_write_config16(dev, 0xa6, 0xA07A);
++ pci_write_config16(dev, 0xa8, 0xA07A);
++ pci_write_config16(dev, 0xaa, 0xA07A);
+ }
+
+ static void sata_init(struct device *dev)
+@@ -83,8 +85,18 @@ static void sata_init(struct device *dev)
+ u32 dword;
+ u8 rev_id;
+ void *sata_bar5;
+- u16 sata_bar0, sata_bar1, sata_bar2, sata_bar3, sata_bar4;
++ uint16_t sata_bar0, sata_bar1, sata_bar2, sata_bar3, sata_bar4;
++ uint16_t ide_bar0, ide_bar1, ide_bar2, ide_bar3;
++ uint16_t current_bar;
+ int i, j;
++ uint8_t nvram;
++ uint8_t sata_ahci_mode;
++ uint8_t port_count;
++ uint8_t max_port_count;
++
++ sata_ahci_mode = 0;
++ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
++ sata_ahci_mode = !!nvram;
+
+ device_t sm_dev;
+ /* SATA SMBus Disable */
+@@ -98,21 +110,39 @@ static void sata_init(struct device *dev)
+ byte |= (1 << 5);
+ pci_write_config8(sm_dev, 0xad, byte);
+
++ /* get rev_id */
++ rev_id = pci_read_config8(sm_dev, 0x08) - 0x28;
++
++ if (sata_ahci_mode) {
++ /* Enable link latency enhancement on A14 and above */
++ if (rev_id >= 0x14) {
++ byte = pci_read_config8(sm_dev, 0xad);
++ byte &= ~(1 << 5);
++ pci_write_config8(sm_dev, 0xad, byte);
++ }
++ }
++
++ /* Disable combined mode */
++ byte = pci_read_config8(sm_dev, 0xad);
++ byte &= ~(1 << 3);
++ pci_write_config8(sm_dev, 0xad, byte);
++
++ device_t ide_dev;
++ /* IDE Device */
++ ide_dev = dev_find_slot(0, PCI_DEVFN(0x14, 1));
++
+ /* RPR 7.2 SATA Initialization */
+ /* Set the interrupt Mapping to INTG# */
+ byte = pci_read_config8(sm_dev, 0xaf);
+ byte = 0x6 << 2;
+ pci_write_config8(sm_dev, 0xaf, byte);
+
+- /* get rev_id */
+- rev_id = pci_read_config8(sm_dev, 0x08) - 0x28;
+-
+ /* get base address */
+ sata_bar5 = (void *)(pci_read_config32(dev, 0x24) & ~0x3FF);
+ sata_bar0 = pci_read_config16(dev, 0x10) & ~0x7;
+ sata_bar1 = pci_read_config16(dev, 0x14) & ~0x3;
+ sata_bar2 = pci_read_config16(dev, 0x18) & ~0x7;
+- sata_bar3 = pci_read_config16(dev, 0x1C) & ~0x3;
++ sata_bar3 = pci_read_config16(dev, 0x1c) & ~0x3;
+ sata_bar4 = pci_read_config16(dev, 0x20) & ~0xf;
+
+ printk(BIOS_SPEW, "sata_bar0=%x\n", sata_bar0); /* 3030 */
+@@ -122,11 +152,16 @@ static void sata_init(struct device *dev)
+ printk(BIOS_SPEW, "sata_bar4=%x\n", sata_bar4); /* 3000 */
+ printk(BIOS_SPEW, "sata_bar5=%p\n", sata_bar5); /* e0309000 */
+
+- /* disable combined mode */
+- byte = pci_read_config8(sm_dev, 0xAD);
+- byte &= ~(1 << 3);
+- pci_write_config8(sm_dev, 0xAD, byte);
+- /* Program the 2C to 0x43801002 */
++ ide_bar0 = pci_read_config16(ide_dev, 0x10) & ~0x7;
++ ide_bar1 = pci_read_config16(ide_dev, 0x14) & ~0x3;
++ ide_bar2 = pci_read_config16(ide_dev, 0x18) & ~0x7;
++ ide_bar3 = pci_read_config16(ide_dev, 0x1c) & ~0x3;
++ printk(BIOS_SPEW, "ide_bar0=%x\n", ide_bar0);
++ printk(BIOS_SPEW, "ide_bar1=%x\n", ide_bar1);
++ printk(BIOS_SPEW, "ide_bar2=%x\n", ide_bar2);
++ printk(BIOS_SPEW, "ide_bar3=%x\n", ide_bar3);
++
++ /* Program the Subsystem ID/VID to 0x43801002 */
+ dword = 0x43801002;
+ pci_write_config32(dev, 0x2c, dword);
+
+@@ -140,15 +175,48 @@ static void sata_init(struct device *dev)
+ byte |= (1 << 2);
+ pci_write_config8(dev, 0x40, byte);
+
+- /* Set SATA Operation Mode, Set to IDE mode */
++ /* Unlock subclass and certain BAR R/O registers */
+ byte = pci_read_config8(dev, 0x40);
+ byte |= (1 << 0);
+- byte |= (1 << 4);
+ pci_write_config8(dev, 0x40, byte);
+
+- dword = 0x01018f00;
+- pci_write_config32(dev, 0x8, dword);
++ /* Disable AHCI enhancement (AMD SP5100 RPR page 54) */
++ dword = pci_read_config32(dev, 0x40);
++ dword |= (1 << 23);
++ pci_write_config32(dev, 0x40, dword);
++
++ if (sata_ahci_mode) {
++ /* Force number of ports to 6
++ * NOTE: This is not documented in the register
++ * reference guide, but CIMX needs to do this
++ * to activate all 6 ports when IDE is disabled.
++ */
++ dword = read32(sata_bar5 + 0x00);
++ dword &= ~0x7;
++ dword |= 0x5;
++ write32(sata_bar5 + 0x00, dword);
++ } else {
++ /* Set SATA Operation Mode, Set to IDE mode */
++ byte = pci_read_config8(dev, 0x40);
++ byte |= (1 << 4);
++ pci_write_config8(dev, 0x40, byte);
++
++ dword = 0x01018f00;
++ pci_write_config32(dev, 0x8, dword);
++ }
++
++ /* Get maximum number of ports */
++ max_port_count = read32(sata_bar5 + 0x00) & 0x1f;
++ max_port_count++;
++ printk(BIOS_SPEW, "Maximum SATA port count supported by silicon: %d\n", max_port_count);
++
++ /* Set number of ports */
++ dword = CONFIG_SOUTHBRIDGE_AMD_SB700_SATA_PORT_COUNT_BITFIELD;
++ for (i = max_port_count; i < 32; i++)
++ dword &= ~(0x1 << i);
++ write32(sata_bar5 + 0x0c, dword);
+
++ /* Write protect Sub-Class Code */
+ byte = pci_read_config8(dev, 0x40);
+ byte &= ~(1 << 0);
+ pci_write_config8(dev, 0x40, byte);
+@@ -181,6 +249,7 @@ static void sata_init(struct device *dev)
+ byte = 0x10;
+ pci_write_config8(dev, 0x46, byte);
+ sb7xx_51xx_setup_sata_phys(dev);
++
+ /* Enable the I/O, MM, BusMaster access for SATA */
+ byte = pci_read_config8(dev, 0x4);
+ byte |= 7 << 0;
+@@ -191,62 +260,75 @@ static void sata_init(struct device *dev)
+ pci_write_config32(dev, 0xC, 0x00004000);
+ #endif
+
+- /* RPR7.7 SATA drive detection. */
+- /* Use BAR5+0x128,BAR0 for Primary Slave */
+- /* Use BAR5+0x1A8,BAR0 for Primary Slave */
+- /* Use BAR5+0x228,BAR2 for Secondary Master */
+- /* Use BAR5+0x2A8,BAR2 for Secondary Slave */
+- /* Use BAR5+0x328,PATA_BAR0/2 for Primary/Secondary master emulation */
+- /* Use BAR5+0x3A8,PATA_BAR0/2 for Primary/Secondary Slave emulation */
+-
+- /* TODO: port 4,5, which are PATA emulations. What are PATA_BARs? */
+-
+- for (i = 0; i < 4; i++) {
+- byte = read8(sata_bar5 + 0x128 + 0x80 * i);
+- printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
+- byte &= 0xF;
+- if( byte == 0x1 ) {
+- /* If the drive status is 0x1 then we see it but we aren't talking to it. */
+- /* Try to do something about it. */
+- printk(BIOS_SPEW, "SATA device detected but not talking. Trying lower speed.\n");
+-
+- /* Read in Port-N Serial ATA Control Register */
+- byte = read8(sata_bar5 + 0x12C + 0x80 * i);
+-
+- /* Set Reset Bit and 1.5g bit */
+- byte |= 0x11;
+- write8((sata_bar5 + 0x12C + 0x80 * i), byte);
+-
+- /* Wait 1ms */
+- mdelay(1);
+-
+- /* Clear Reset Bit */
+- byte &= ~0x01;
+- write8((sata_bar5 + 0x12C + 0x80 * i), byte);
+-
+- /* Wait 1ms */
+- mdelay(1);
+-
+- /* Reread status */
++ /* Determine port count */
++ port_count = 0;
++ for (i = 0; i < 32; i++) {
++ if (CONFIG_SOUTHBRIDGE_AMD_SB700_SATA_PORT_COUNT_BITFIELD & (0x1 << i))
++ port_count = i;
++ }
++ port_count++;
++ if (port_count > max_port_count)
++ port_count = max_port_count;
++
++ if (!sata_ahci_mode) {
++ /* RPR7.7 SATA drive detection. */
++ /* Use BAR5+0x128,BAR0 for Primary Slave */
++ /* Use BAR5+0x1A8,BAR0 for Primary Slave */
++ /* Use BAR5+0x228,BAR2 for Secondary Master */
++ /* Use BAR5+0x2A8,BAR2 for Secondary Slave */
++ /* Use BAR5+0x328,PATA_BAR0/2 for Primary/Secondary Master emulation */
++ /* Use BAR5+0x3A8,PATA_BAR0/2 for Primary/Secondary Slave emulation */
++ for (i = 0; i < port_count; i++) {
+ byte = read8(sata_bar5 + 0x128 + 0x80 * i);
+ printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
+ byte &= 0xF;
+- }
+-
+- if (byte == 0x3) {
+- for (j = 0; j < 10; j++) {
+- if (!sata_drive_detect(i, ((i / 2) == 0) ? sata_bar0 : sata_bar2))
+- break;
++ if (byte == 0x1) {
++ /* If the drive status is 0x1 then we see it but we aren't talking to it. */
++ /* Try to do something about it. */
++ printk(BIOS_SPEW, "SATA device detected but not talking. Trying lower speed.\n");
++
++ /* Read in Port-N Serial ATA Control Register */
++ byte = read8(sata_bar5 + 0x12C + 0x80 * i);
++
++ /* Set Reset Bit and 1.5g bit */
++ byte |= 0x11;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++
++ /* Clear Reset Bit */
++ byte &= ~0x01;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++
++ /* Reread status */
++ byte = read8(sata_bar5 + 0x128 + 0x80 * i);
++ printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
++ byte &= 0xF;
++ }
++
++ if (byte == 0x3) {
++ for (j = 0; j < 10; j++) {
++ if (i < 4)
++ current_bar = ((i / 2) == 0) ? sata_bar0 : sata_bar2;
++ else
++ current_bar = ide_bar0;
++ if (!sata_drive_detect(i, current_bar))
++ break;
++ }
++ printk(BIOS_DEBUG, "%s %s device is %sready after %i tries\n",
++ (i / 2) ? "Secondary" : "Primary",
++ (i % 2 ) ? "Slave" : "Master",
++ (j == 10) ? "not " : "",
++ (j == 10) ? j : j + 1);
++ } else {
++ printk(BIOS_DEBUG, "No %s %s SATA drive on Slot%i\n",
++ (i / 2) ? "Secondary" : "Primary",
++ (i % 2 ) ? "Slave" : "Master", i);
+ }
+- printk(BIOS_DEBUG, "%s %s device is %sready after %i tries\n",
+- (i / 2) ? "Secondary" : "Primary",
+- (i % 2 ) ? "Slave" : "Master",
+- (j == 10) ? "not " : "",
+- (j == 10) ? j : j + 1);
+- } else {
+- printk(BIOS_DEBUG, "No %s %s SATA drive on Slot%i\n",
+- (i / 2) ? "Secondary" : "Primary",
+- (i % 2 ) ? "Slave" : "Master", i);
+ }
+ }
+
+@@ -256,13 +338,15 @@ static void sata_init(struct device *dev)
+ byte |= 1 << 1;
+ write8((sata_bar5 + 0x4), byte);
+
+- /* Clear error status */
+- write32((sata_bar5 + 0x130), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x1b0), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x230), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x2b0), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x330), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x3b0), 0xFFFFFFFF);
++ if (!sata_ahci_mode) {
++ /* Clear error status */
++ write32((sata_bar5 + 0x130), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x1b0), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x230), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x2b0), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x330), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x3b0), 0xFFFFFFFF);
++ }
+
+ /* Clear SATA status,Firstly we get the AcpiGpe0BlkAddr */
+ /* ????? why CIM does not set the AcpiGpe0BlkAddr , but use it??? */
+@@ -293,3 +377,15 @@ static const struct pci_driver sata0_driver __pci_driver = {
+ .vendor = PCI_VENDOR_ID_ATI,
+ .device = PCI_DEVICE_ID_ATI_SB700_SATA,
+ };
++
++static const struct pci_driver sata1_driver __pci_driver = {
++ .ops = &sata_ops,
++ .vendor = PCI_VENDOR_ID_ATI,
++ .device = PCI_DEVICE_ID_ATI_SB700_SATA_AHCI,
++};
++
++static const struct pci_driver sata2_driver __pci_driver = {
++ .ops = &sata_ops,
++ .vendor = PCI_VENDOR_ID_ATI,
++ .device = PCI_DEVICE_ID_ATI_SB700_SATA_AHCI_AMD,
++};
+diff --git a/src/southbridge/amd/sb800/fadt.c b/src/southbridge/amd/sb800/fadt.c
+index 5250e20..95e3354 100644
+--- a/src/southbridge/amd/sb800/fadt.c
++++ b/src/southbridge/amd/sb800/fadt.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0051-mainboard-asus-kgpe-d16-Properly-initialize-SB700-SA.patch b/resources/libreboot/patch/kgpe-d16/0051-mainboard-asus-kgpe-d16-Properly-initialize-SB700-SA.patch
new file mode 100644
index 00000000..6113a16d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0051-mainboard-asus-kgpe-d16-Properly-initialize-SB700-SA.patch
@@ -0,0 +1,47 @@
+From c76661067c11f2ba9a7b58a7cb87215cea2a3f61 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 9 Jun 2015 18:57:23 -0500
+Subject: [PATCH 051/146] mainboard/asus/kgpe-d16: Properly initialize SB700
+ SATA PHYs
+
+---
+ src/mainboard/asus/kgpe-d16/mainboard.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+diff --git a/src/mainboard/asus/kgpe-d16/mainboard.c b/src/mainboard/asus/kgpe-d16/mainboard.c
+index 47ede34..8de6f26 100644
+--- a/src/mainboard/asus/kgpe-d16/mainboard.c
++++ b/src/mainboard/asus/kgpe-d16/mainboard.c
+@@ -76,6 +76,29 @@ static void mainboard_enable(device_t dev)
+ /* get_ide_dma66(); */
+ }
+
++/* override the default SATA PHY setup */
++void sb7xx_51xx_setup_sata_phys(struct device *dev)
++{
++ /* RPR7.6.1 Program the PHY Global Control to 0x2C00 */
++ pci_write_config16(dev, 0x86, 0x2c00);
++
++ /* RPR7.6.2 SATA GENI PHY ports setting */
++ pci_write_config32(dev, 0x88, 0x01b48016);
++ pci_write_config32(dev, 0x8c, 0x01b48016);
++ pci_write_config32(dev, 0x90, 0x01b48016);
++ pci_write_config32(dev, 0x94, 0x01b48016);
++ pci_write_config32(dev, 0x98, 0x01b48016);
++ pci_write_config32(dev, 0x9c, 0x01b48016);
++
++ /* RPR7.6.3 SATA GEN II PHY port setting for port [0~5]. */
++ pci_write_config16(dev, 0xa0, 0xa07a);
++ pci_write_config16(dev, 0xa2, 0xa07a);
++ pci_write_config16(dev, 0xa4, 0xa07a);
++ pci_write_config16(dev, 0xa6, 0xa07a);
++ pci_write_config16(dev, 0xa8, 0xa07a);
++ pci_write_config16(dev, 0xaa, 0xa07a);
++}
++
+ struct chip_operations mainboard_ops = {
+ .enable_dev = mainboard_enable,
+ };
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0052-southbridge-amd-sb700-Disable-broken-SATA-MSI-functi.patch b/resources/libreboot/patch/kgpe-d16/0052-southbridge-amd-sb700-Disable-broken-SATA-MSI-functi.patch
new file mode 100644
index 00000000..01ff80a6
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0052-southbridge-amd-sb700-Disable-broken-SATA-MSI-functi.patch
@@ -0,0 +1,39 @@
+From 173148993ede860eb9e00d5d6efee322f6b03424 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 9 Jun 2015 19:12:35 -0500
+Subject: [PATCH 052/146] southbridge/amd/sb700: Disable broken SATA MSI
+ functionality
+
+---
+ src/southbridge/amd/sb700/early_setup.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index 70a2aee..1f92a4e 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -678,6 +678,7 @@ static void sb700_pci_cfg(void)
+ {
+ device_t dev;
+ u8 byte;
++ uint8_t acpi_s1_supported = 1;
+
+ /* SMBus Device, BDF:0-20-0 */
+ dev = pci_locate_device(PCI_ID(0x1002, 0x4385), 0);
+@@ -730,10 +731,10 @@ static void sb700_pci_cfg(void)
+ byte = pci_read_config8(dev, 0x40);
+ byte |= 1 << 0;
+ pci_write_config8(dev, 0x40, byte);
+- if (get_sb700_revision(pci_locate_device(PCI_ID(0x1002, 0x4385), 0)) <= 0x12)
+- pci_write_config8(dev, 0x34, 0x70); /* set 0x61 to 0x70 if S1 is not supported. */
++ if (acpi_s1_supported)
++ pci_write_config8(dev, 0x34, 0x70); /* Hide D3 power state and MSI capabilities */
+ else
+- pci_write_config8(dev, 0x34, 0x50); /* set 0x61 to 0x50 if S1 is not supported. */
++ pci_write_config8(dev, 0x61, 0x70); /* Hide MSI capability */
+ byte &= ~(1 << 0);
+ pci_write_config8(dev, 0x40, byte);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0053-southbridge-amd-sb700-Indicate-iSATA-eSATA-port-type.patch b/resources/libreboot/patch/kgpe-d16/0053-southbridge-amd-sb700-Indicate-iSATA-eSATA-port-type.patch
new file mode 100644
index 00000000..bb770ae9
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0053-southbridge-amd-sb700-Indicate-iSATA-eSATA-port-type.patch
@@ -0,0 +1,92 @@
+From 657c3cc04be3d40e735acc460968950a3400f302 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 9 Jun 2015 19:34:16 -0500
+Subject: [PATCH 053/146] southbridge/amd/sb700: Indicate iSATA/eSATA port
+ type
+
+---
+ src/mainboard/asus/kgpe-d16/mainboard.c | 16 ++++++++++++++++
+ src/southbridge/amd/sb700/sata.c | 19 +++++++++++++++++++
+ src/southbridge/amd/sb700/sb700.h | 1 +
+ 3 files changed, 36 insertions(+)
+
+diff --git a/src/mainboard/asus/kgpe-d16/mainboard.c b/src/mainboard/asus/kgpe-d16/mainboard.c
+index 8de6f26..77e55db 100644
+--- a/src/mainboard/asus/kgpe-d16/mainboard.c
++++ b/src/mainboard/asus/kgpe-d16/mainboard.c
+@@ -99,6 +99,22 @@ void sb7xx_51xx_setup_sata_phys(struct device *dev)
+ pci_write_config16(dev, 0xaa, 0xa07a);
+ }
+
++/* override the default SATA port setup */
++void sb7xx_51xx_setup_sata_port_indication(void *sata_bar5)
++{
++ uint32_t dword;
++
++ /* RPR7.9 Program Port Indication Registers */
++ dword = read32(sata_bar5 + 0xf8);
++ dword &= ~(0x3f << 12); /* All ports are iSATA */
++ dword &= ~0x3f;
++ write32(sata_bar5 + 0xf8, dword);
++
++ dword = read32(sata_bar5 + 0xfc);
++ dword &= ~(0x1 << 20); /* No eSATA ports are present */
++ write32(sata_bar5 + 0xfc, dword);
++}
++
+ struct chip_operations mainboard_ops = {
+ .enable_dev = mainboard_enable,
+ };
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index c0b3cd5..dda2fc3 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -78,6 +78,23 @@ void __attribute__((weak)) sb7xx_51xx_setup_sata_phys(struct device *dev)
+ pci_write_config16(dev, 0xaa, 0xA07A);
+ }
+
++/* This function can be overloaded in mainboard.c */
++void __attribute__((weak)) sb7xx_51xx_setup_sata_port_indication(void *sata_bar5)
++{
++ uint32_t dword;
++
++ /* RPR7.9 Program Port Indication Registers */
++ dword = read32(sata_bar5 + 0xf8);
++ dword &= ~(0x3f << 12); /* Ports 0 and 1 are eSATA */
++ dword |= (0x3 << 12);
++ dword &= ~0x3f;
++ write32(sata_bar5 + 0xf8, dword);
++
++ dword = read32(sata_bar5 + 0xfc);
++ dword |= 0x1 << 20; /* At least one eSATA port is present */
++ write32(sata_bar5 + 0xfc, dword);
++}
++
+ static void sata_init(struct device *dev)
+ {
+ u8 byte;
+@@ -248,7 +265,9 @@ static void sata_init(struct device *dev)
+ /* Program the watchdog counter to 0x10 */
+ byte = 0x10;
+ pci_write_config8(dev, 0x46, byte);
++
+ sb7xx_51xx_setup_sata_phys(dev);
++ sb7xx_51xx_setup_sata_port_indication(sata_bar5);
+
+ /* Enable the I/O, MM, BusMaster access for SATA */
+ byte = pci_read_config8(dev, 0x4);
+diff --git a/src/southbridge/amd/sb700/sb700.h b/src/southbridge/amd/sb700/sb700.h
+index 941a4fd..8f792e7 100644
+--- a/src/southbridge/amd/sb700/sb700.h
++++ b/src/southbridge/amd/sb700/sb700.h
+@@ -74,6 +74,7 @@ void sb7xx_51xx_before_pci_init(void);
+ #include <device/pci.h>
+ /* allow override in mainboard.c */
+ void sb7xx_51xx_setup_sata_phys(struct device *dev);
++void sb7xx_51xx_setup_sata_port_indication(void *sata_bar5);
+
+ #endif
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0054-northbridge-amd-amdfam10-Add-ability-to-set-maximum-.patch b/resources/libreboot/patch/kgpe-d16/0054-northbridge-amd-amdfam10-Add-ability-to-set-maximum-.patch
new file mode 100644
index 00000000..09aa956d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0054-northbridge-amd-amdfam10-Add-ability-to-set-maximum-.patch
@@ -0,0 +1,90 @@
+From 9e4833cba06cfb3b771f2d6472996534abb082fd Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Wed, 10 Jun 2015 00:35:05 -0500
+Subject: [PATCH 054/146] northbridge/amd/amdfam10: Add ability to set maximum
+ P-state limit
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 3 ++-
+ src/northbridge/amd/amdfam10/misc_control.c | 25 +++++++++++++++++++++----
+ 3 files changed, 24 insertions(+), 5 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 5bfaadd..a52b7fa 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -17,6 +17,7 @@ interleave_memory_channels = Enable
+ cpu_c_states = Enable
+ cpu_cc6_state = Enable
+ sata_ahci_mode = Enable
++maximum_p_state_limit = 0xf
+ ieee1394 = Enable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 247fd7b..307bddc 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -46,7 +46,8 @@ entries
+ 465 1 e 1 cpu_c_states
+ 466 1 e 1 cpu_cc6_state
+ 467 1 e 1 sata_ahci_mode
+-468 1 r 0 allow_spd_nvram_cache_restore
++468 4 h 0 maximum_p_state_limit
++473 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index 8777e8f..1057ac1 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -32,6 +32,7 @@
+ #include <device/pci_ids.h>
+ #include <device/pci_ops.h>
+ #include <pc80/mc146818rtc.h>
++#include <option.h>
+ #include <lib.h>
+ #include <cpu/amd/model_10xxx_rev.h>
+
+@@ -124,16 +125,32 @@ static void mcf3_set_resources(device_t dev)
+
+ static void misc_control_init(struct device *dev)
+ {
+- u32 cmd;
++ uint32_t dword;
++ uint8_t nvram;
++ uint8_t boost_limit;
++ uint8_t current_boost;
+
+ printk(BIOS_DEBUG, "NB: Function 3 Misc Control.. ");
+
+ /* Disable Machine checks from Invalid Locations.
+ * This is needed for PC backwards compatibility.
+ */
+- cmd = pci_read_config32(dev, 0x44);
+- cmd |= (1<<6) | (1<<25);
+- pci_write_config32(dev, 0x44, cmd );
++ dword = pci_read_config32(dev, 0x44);
++ dword |= (1<<6) | (1<<25);
++ pci_write_config32(dev, 0x44, dword);
++
++ boost_limit = 0xf;
++ if (get_option(&nvram, "maximum_p_state_limit") == CB_SUCCESS)
++ boost_limit = nvram & 0xf;
++
++ /* Set P-state maximum value */
++ dword = pci_read_config32(dev, 0xdc);
++ current_boost = (dword >> 8) & 0x7;
++ if (boost_limit > current_boost)
++ boost_limit = current_boost;
++ dword &= ~(0x7 << 8);
++ dword |= (boost_limit & 0x7) << 8;
++ pci_write_config32(dev, 0xdc, dword);
+
+ printk(BIOS_DEBUG, "done.\n");
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0055-northbridge-amd-amdmct-Verify-MCT-NVRAM-options-befo.patch b/resources/libreboot/patch/kgpe-d16/0055-northbridge-amd-amdmct-Verify-MCT-NVRAM-options-befo.patch
new file mode 100644
index 00000000..5de14825
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0055-northbridge-amd-amdmct-Verify-MCT-NVRAM-options-befo.patch
@@ -0,0 +1,143 @@
+From 1786c1ae0d378df7f2a0f99e13d32df619e0f010 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Wed, 10 Jun 2015 10:46:17 -0500
+Subject: [PATCH 055/146] northbridge/amd/amdmct: Verify MCT NVRAM options
+ before skipping training
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 5 +++-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 2 ++
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 36 ++++++++++++++++++++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.h | 3 ++-
+ 4 files changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 6d8c23e..e493158 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -4119,7 +4119,7 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+ calculate_and_store_spd_hashes(pMCTstat, pDCTstat);
+
+ #if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
+- if (load_spd_hashes_from_nvram(pDCTstat) < 0) {
++ if (load_spd_hashes_from_nvram(pMCTstat, pDCTstat) < 0) {
+ pDCTstat->spd_data.nvram_spd_match = 0;
+ }
+ else {
+@@ -4134,6 +4134,9 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+ if (get_option(&nvram, "allow_spd_nvram_cache_restore") == CB_SUCCESS)
+ allow_config_restore = !!nvram;
+
++ if (pMCTstat->nvram_checksum != calculate_nvram_mct_hash())
++ allow_config_restore = 0;
++
+ if (!allow_config_restore)
+ pDCTstat->spd_data.nvram_spd_match = 0;
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index adf89b2..11555ae 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -301,6 +301,7 @@ struct MCTStatStruc {
+ u32 Sub4GCacheTop; /* If not zero, the 32-bit top of cacheable memory.*/
+ u32 SysLimit; /* LIMIT[39:8] (system address)*/
+ uint32_t TSCFreq;
++ uint16_t nvram_checksum;
+ } __attribute__((packed));
+
+ /*=============================================================================
+@@ -796,6 +797,7 @@ struct amd_s3_persistent_node_data {
+
+ struct amd_s3_persistent_data {
+ struct amd_s3_persistent_node_data node[MAX_NODES_SUPPORTED];
++ uint16_t nvram_checksum;
+ } __attribute__((packed));
+
+ /*===============================================================================
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 4c0e58d..2132648 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -143,6 +143,36 @@ void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash)
+ *spd_hash = *spd_hash ^ (*spd_hash << 37);
+ }
+
++uint16_t calculate_nvram_mct_hash(void)
++{
++ uint32_t nvram;
++ uint16_t ret;
++
++ ret = 0;
++ if (get_option(&nvram, "max_mem_clock") == CB_SUCCESS)
++ ret |= nvram & 0xf;
++ if (get_option(&nvram, "minimum_memory_voltage") == CB_SUCCESS)
++ ret |= (nvram & 0x3) << 4;
++ if (get_option(&nvram, "ECC_memory") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 6;
++ if (get_option(&nvram, "ECC_redirection") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 7;
++ if (get_option(&nvram, "ecc_scrub_rate") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 8;
++ if (get_option(&nvram, "interleave_chip_selects") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 9;
++ if (get_option(&nvram, "interleave_nodes") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 10;
++ if (get_option(&nvram, "interleave_memory_channels") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 11;
++ if (get_option(&nvram, "cpu_c_states") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 12;
++ if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
++ ret |= (nvram & 0x1) << 13;
++
++ return ret;
++}
++
+ static struct amd_s3_persistent_data * map_s3nv_in_nvram(void)
+ {
+ ssize_t s3nv_offset;
+@@ -173,7 +203,7 @@ static struct amd_s3_persistent_data * map_s3nv_in_nvram(void)
+ }
+
+ #ifdef __PRE_RAM__
+-int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat)
++int8_t load_spd_hashes_from_nvram(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+ {
+ struct amd_s3_persistent_data *persistent_data;
+
+@@ -184,6 +214,8 @@ int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat)
+ memcpy(pDCTstat->spd_data.nvram_spd_hash, persistent_data->node[pDCTstat->Node_ID].spd_hash, sizeof(pDCTstat->spd_data.nvram_spd_hash));
+ memcpy(pDCTstat->spd_data.nvram_memclk, persistent_data->node[pDCTstat->Node_ID].memclk, sizeof(pDCTstat->spd_data.nvram_memclk));
+
++ pMCTstat->nvram_checksum = persistent_data->nvram_checksum;
++
+ return 0;
+ }
+ #endif
+@@ -237,6 +269,8 @@ static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data*
+ for (channel = 0; channel < 2; channel++)
+ persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed;
+
++ persistent_data->nvram_checksum = calculate_nvram_mct_hash();
++
+ if (restored) {
+ if (mem_info->mct_stat.GStatus & (1 << GSB_ConfigRestored))
+ *restored = 1;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+index 82f73a7..74922c4 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h
+@@ -21,9 +21,10 @@
+ #include "mct_d.h"
+
+ void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash);
++uint16_t calculate_nvram_mct_hash(void);
+
+ #ifdef __PRE_RAM__
+-int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat);
++int8_t load_spd_hashes_from_nvram(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ #endif
+
+ #ifdef __RAMSTAGE__
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0056-src-northbridge-amd-amdmct-Add-option-to-override-ba.patch b/resources/libreboot/patch/kgpe-d16/0056-src-northbridge-amd-amdmct-Add-option-to-override-ba.patch
new file mode 100644
index 00000000..be380140
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0056-src-northbridge-amd-amdmct-Add-option-to-override-ba.patch
@@ -0,0 +1,106 @@
+From 7c25f47057008eede54dd92a7242052b6ec3b479 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 11 Jun 2015 16:14:15 -0500
+Subject: [PATCH 056/146] src/northbridge/amd/amdmct: Add option to override
+ bad SPD checksum
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 7 ++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 7 ++++---
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 8 ++++++++
+ 4 files changed, 19 insertions(+), 4 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index a52b7fa..73f2a38 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -8,6 +8,7 @@ nmi = Disable
+ hypertransport_speed_limit = Auto
+ max_mem_clock = DDR3-1600
+ minimum_memory_voltage = 1.5V
++dimm_spd_checksum = Enforce
+ ECC_memory = Enable
+ ECC_redirection = Enable
+ ecc_scrub_rate = 1.28us
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 307bddc..e91568c 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -47,8 +47,10 @@ entries
+ 466 1 e 1 cpu_cc6_state
+ 467 1 e 1 sata_ahci_mode
+ 468 4 h 0 maximum_p_state_limit
+-473 1 r 0 allow_spd_nvram_cache_restore
++472 2 e 13 dimm_spd_checksum
++474 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
++>>>>>>> bed9a97... src/northbridge/amd/amdmct: Add option to override bad SPD checksum
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+ # Reserve the extended AMD configuration registers
+@@ -142,6 +144,9 @@ enumerations
+ 12 1 1.35V
+ 12 2 1.25V
+ 12 3 1.15V
++13 0 Enforce
++13 1 Ignore
++13 2 Override
+
+ checksums
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index e493158..28f8d18 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1443,10 +1443,10 @@ restartinit:
+ }
+ }
+ if (NodesWmem == 0) {
+- printk(BIOS_DEBUG, "No Nodes?!\n");
++ printk(BIOS_ALERT, "Unable to detect valid memory on any nodes. Halting!\n");
+ goto fatalexit;
+ }
+-
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+
+@@ -3877,13 +3877,14 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ read_spd_bytes(pMCTstat, pDCTstat, i);
+ crc_status = crcCheck(pDCTstat, i);
+ }
+- if (crc_status) { /* CRC is OK */
++ if ((crc_status) || (SPDCtrl == 2)) { /* CRC is OK */
+ byte = pDCTstat->spd_data.spd_bytes[i][SPD_TYPE];
+ if (byte == JED_DDR3SDRAM) {
+ /*Dimm is 'Present'*/
+ pDCTstat->DIMMValid |= 1 << i;
+ }
+ } else {
++ printk(BIOS_WARNING, "Node %d DIMM %d: SPD checksum invalid\n", pDCTstat->Node_ID, i);
+ pDCTstat->DIMMSPDCSE = 1 << i;
+ if (SPDCtrl == 0) {
+ pDCTstat->ErrStatus |= 1 << SB_DIMMChkSum;
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index 2e53f0b..1a4e984 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -150,6 +150,14 @@ static u16 mctGet_NVbits(u8 index)
+ case NV_SPDCHK_RESTRT:
+ val = 0; /* Exit current node initialization if any DIMM has SPD checksum error */
+ //val = 1; /* Ignore faulty SPD checksum (DIMM will still be disabled), continue current node intialization */
++ //val = 2; /* Override faulty SPD checksum (DIMM will be enabled), continue current node intialization */
++
++ if (get_option(&nvram, "dimm_spd_checksum") == CB_SUCCESS)
++ val = nvram & 0x3;
++
++ if (val > 2)
++ val = 2;
++
+ break;
+ case NV_DQSTrainCTL:
+ //val = 0; /*Skip dqs training */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0057-mainboard-asus-kgpe-d16-Add-missing-IRQ-routing-for-.patch b/resources/libreboot/patch/kgpe-d16/0057-mainboard-asus-kgpe-d16-Add-missing-IRQ-routing-for-.patch
new file mode 100644
index 00000000..37bfa24d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0057-mainboard-asus-kgpe-d16-Add-missing-IRQ-routing-for-.patch
@@ -0,0 +1,207 @@
+From ade9e1905927f05cdd4cba50a9127be1ef954af5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 12 Jun 2015 13:32:52 -0500
+Subject: [PATCH 057/146] mainboard/asus/kgpe-d16: Add missing IRQ routing for
+ PIKE card
+
+---
+ src/mainboard/asus/kgpe-d16/dsdt.asl | 74 +++++++++++++++++++++++++---------
+ 1 file changed, 55 insertions(+), 19 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/dsdt.asl b/src/mainboard/asus/kgpe-d16/dsdt.asl
+index b6f10d9..bbe445f 100644
+--- a/src/mainboard/asus/kgpe-d16/dsdt.asl
++++ b/src/mainboard/asus/kgpe-d16/dsdt.asl
+@@ -296,13 +296,29 @@ DefinitionBlock (
+
+ Name (PR03, Package () {
+ /* PIC */
++ Package (0x04) { 0xFFFF, 0x00, LNKE, 0x00 },
++ Package (0x04) { 0xFFFF, 0x01, LNKF, 0x00 },
++ Package (0x04) { 0xFFFF, 0x02, LNKG, 0x00 },
++ Package (0x04) { 0xFFFF, 0x03, LNKH, 0x00 },
++ })
++
++ Name (AR03, Package () {
++ /* APIC */
++ Package (0x04) { 0xFFFF, 0x00, 0x00, 44 },
++ Package (0x04) { 0xFFFF, 0x01, 0x00, 45 },
++ Package (0x04) { 0xFFFF, 0x02, 0x00, 46 },
++ Package (0x04) { 0xFFFF, 0x03, 0x00, 47 },
++ })
++
++ Name (PR04, Package () {
++ /* PIC */
+ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
+ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
+ Package (0x04) { 0xFFFF, 0x02, LNKC, 0x00 },
+ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
+ })
+
+- Name (AR03, Package () {
++ Name (AR04, Package () {
+ /* APIC */
+ Package (0x04) { 0xFFFF, 0x00, 0x00, 48 },
+ Package (0x04) { 0xFFFF, 0x01, 0x00, 49 },
+@@ -310,7 +326,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, 0x00, 51 },
+ })
+
+- Name (PR04, Package () {
++ Name (PR05, Package () {
+ /* PIC */
+ Package (0x04) { 0xFFFF, 0x00, LNKH, 0x00 },
+ Package (0x04) { 0xFFFF, 0x01, LNKE, 0x00 },
+@@ -318,7 +334,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, LNKG, 0x00 },
+ })
+
+- Name (AR04, Package () {
++ Name (AR05, Package () {
+ /* APIC */
+ Package (0x04) { 0xFFFF, 0x00, 0x00, 47 },
+ Package (0x04) { 0xFFFF, 0x01, 0x00, 44 },
+@@ -326,7 +342,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, 0x00, 46 },
+ })
+
+- Name (PR05, Package () {
++ Name (PR06, Package () {
+ /* PIC */
+ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
+ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
+@@ -334,7 +350,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
+ })
+
+- Name (AR05, Package () {
++ Name (AR06, Package () {
+ /* APIC */
+ Package (0x04) { 0xFFFF, 0x00, 0x00, 32 },
+ Package (0x04) { 0xFFFF, 0x01, 0x00, 33 },
+@@ -342,7 +358,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, 0x00, 35 },
+ })
+
+- Name (PR06, Package () {
++ Name (PR07, Package () {
+ /* PIC */
+ Package (0x04) { 0xFFFF, 0x00, LNKE, 0x00 },
+ Package (0x04) { 0xFFFF, 0x01, LNKF, 0x00 },
+@@ -350,7 +366,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, LNKH, 0x00 },
+ })
+
+- Name (AR06, Package () {
++ Name (AR07, Package () {
+ /* APIC */
+ Package (0x04) { 0xFFFF, 0x00, 0x00, 36 },
+ Package (0x04) { 0xFFFF, 0x01, 0x00, 37 },
+@@ -358,7 +374,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, 0x00, 39 },
+ })
+
+- Name (PR07, Package () {
++ Name (PR08, Package () {
+ /* PIC */
+ Package (0x04) { 0xFFFF, 0x00, LNKA, 0x00 },
+ Package (0x04) { 0xFFFF, 0x01, LNKB, 0x00 },
+@@ -366,7 +382,7 @@ DefinitionBlock (
+ Package (0x04) { 0xFFFF, 0x03, LNKD, 0x00 },
+ })
+
+- Name (AR07, Package () {
++ Name (AR08, Package () {
+ /* APIC */
+ Package (0x04) { 0xFFFF, 0x00, 0x00, 40 },
+ Package (0x04) { 0xFFFF, 0x01, 0x00, 41 },
+@@ -617,6 +633,26 @@ DefinitionBlock (
+ }
+ }
+
++ /* 1:00.0 PIKE */
++ Device (PIKE)
++ {
++ Name (_ADR, 0x00040000) // _ADR: Address
++ Name(_PRW, Package () {0x11, 0x04}) // Wake from S1-S4
++ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
++ {
++ If (PICM) {
++ Return (AR03)
++ } Else {
++ Return (PR03)
++ }
++ }
++ Device (SLT1)
++ {
++ Name (_ADR, 0xFFFF) // _ADR: Address
++ Name(_PRW, Package () {0x0B, 0x04}) // Wake from S1-S4
++ }
++ }
++
+ /* 3:00.0 PCIe NIC A */
+ Device (NICA)
+ {
+@@ -625,9 +661,9 @@ DefinitionBlock (
+ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
+ {
+ If (PICM) {
+- Return (AR03)
++ Return (AR04)
+ } Else {
+- Return (PR03)
++ Return (PR04)
+ }
+ }
+ Device (BDC1)
+@@ -644,9 +680,9 @@ DefinitionBlock (
+ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
+ {
+ If (PICM) {
+- Return (AR04)
++ Return (AR05)
+ } Else {
+- Return (PR04)
++ Return (PR05)
+ }
+ }
+ Device (BDC2)
+@@ -663,9 +699,9 @@ DefinitionBlock (
+ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
+ {
+ If (PICM) {
+- Return (AR05)
++ Return (AR06)
+ } Else {
+- Return (PR05)
++ Return (PR06)
+ }
+ }
+ Device (SLT1)
+@@ -683,9 +719,9 @@ DefinitionBlock (
+ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
+ {
+ If (PICM) {
+- Return (AR06)
++ Return (AR07)
+ } Else {
+- Return (PR06)
++ Return (PR07)
+ }
+ }
+ Device (SLT1)
+@@ -703,9 +739,9 @@ DefinitionBlock (
+ Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table
+ {
+ If (PICM) {
+- Return (AR07)
++ Return (AR08)
+ } Else {
+- Return (PR07)
++ Return (PR08)
+ }
+ }
+ Device (SLT1)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0058-northbridge-amd-amdmct-Fix-hang-on-boot-due-to-inval.patch b/resources/libreboot/patch/kgpe-d16/0058-northbridge-amd-amdmct-Fix-hang-on-boot-due-to-inval.patch
new file mode 100644
index 00000000..3dfd6e1e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0058-northbridge-amd-amdmct-Fix-hang-on-boot-due-to-inval.patch
@@ -0,0 +1,35 @@
+From 89a7158d49583b376c9ba34d8b180d8a99f6aa84 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 12 Jun 2015 19:43:06 -0500
+Subject: [PATCH 058/146] northbridge/amd/amdmct: Fix hang on boot due to
+ invalid array access
+
+---
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index 1a4e984..d1da07b 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -344,7 +344,7 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ }
+
+ #if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+- for (i = 0; i < 15; i = i + 2) {
++ for (i = 0; i < MAX_DIMMS_SUPPORTED; i = i + 2) {
+ if (pDCTstat->DIMMValid & (1 << i))
+ ch1_voltage |= pDCTstat->DimmConfiguredVoltage[i];
+ if (pDCTstat->DIMMValid & (1 << (i + 1)))
+@@ -355,7 +355,7 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ for (i = 0; i < 2; i++) {
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[i];
+ highest_rank_count[i] = 0x0;
+- for (dimm = 0; dimm < 8; dimm++) {
++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
+ if (pDCTData->DimmRanks[dimm] > highest_rank_count[i])
+ highest_rank_count[i] = pDCTData->DimmRanks[dimm];
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0059-southbridge-amd-sr5650-Fix-GPP3a-link-training-in-hi.patch b/resources/libreboot/patch/kgpe-d16/0059-southbridge-amd-sr5650-Fix-GPP3a-link-training-in-hi.patch
new file mode 100644
index 00000000..095f3e7d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0059-southbridge-amd-sr5650-Fix-GPP3a-link-training-in-hi.patch
@@ -0,0 +1,83 @@
+From d70984fcd30907e57adb6017e0c36b295b354be3 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 12 Jun 2015 19:43:38 -0500
+Subject: [PATCH 059/146] southbridge/amd/sr5650: Fix GPP3a link training in
+ higher width modes
+
+---
+ src/southbridge/amd/sr5650/pcie.c | 51 ++++++++++++++++++++++++++++++++++---
+ 1 file changed, 47 insertions(+), 4 deletions(-)
+
+diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
+index d306b5a..79f2a5f 100644
+--- a/src/southbridge/amd/sr5650/pcie.c
++++ b/src/southbridge/amd/sr5650/pcie.c
+@@ -249,7 +249,7 @@ static void switching_gpp3a_configurations(device_t nb_dev, device_t sb_dev)
+ reg |= 0xFF0BAA0;
+ break;
+ default: /* shouldn't be here. */
+- printk(BIOS_DEBUG, "Warning:gpp3a_configuration is not correct. Check you devicetree.cb\n");
++ printk(BIOS_DEBUG, "Warning:gpp3a_configuration is not correct. Check your devicetree.cb\n");
+ break;
+ }
+ nbmisc_write_index(nb_dev, 0x26, reg);
+@@ -722,10 +722,53 @@ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
+
+ /* check port enable */
+ if (cfg->port_enable & (1 << port)) {
+- PcieReleasePortTraining(nb_dev, dev, port);
++ uint32_t hw_port = port;
++ switch (cfg->gpp3a_configuration) {
++ case 0x1: /* 4:2:0:0:0:0 */
++ if (hw_port == 9)
++ hw_port = 4 + 1;
++ break;
++ case 0x2: /* 4:1:1:0:0:0 */
++ if (hw_port == 9)
++ hw_port = 4 + 1;
++ else if (hw_port == 10)
++ hw_port = 4 + 2;
++ break;
++ case 0xc: /* 2:2:2:0:0:0 */
++ if (hw_port == 6)
++ hw_port = 4 + 1;
++ else if (hw_port == 9)
++ hw_port = 4 + 2;
++ break;
++ case 0xa: /* 2:2:1:1:0:0 */
++ if (hw_port == 6)
++ hw_port = 4 + 1;
++ else if (hw_port == 9)
++ hw_port = 4 + 2;
++ else if (hw_port == 10)
++ hw_port = 4 + 3;
++ break;
++ case 0x4: /* 2:1:1:1:1:0 */
++ if (hw_port == 6)
++ hw_port = 4 + 1;
++ else if (hw_port == 7)
++ hw_port = 4 + 2;
++ else if (hw_port == 9)
++ hw_port = 4 + 3;
++ else if (hw_port == 10)
++ hw_port = 4 + 4;
++ break;
++ case 0xb: /* 1:1:1:1:1:1 */
++ break;
++ default: /* shouldn't be here. */
++ printk(BIOS_WARNING, "invalid gpp3a_configuration\n");
++ return;
++ }
++ PcieReleasePortTraining(nb_dev, dev, hw_port);
+ if (!(AtiPcieCfg.Config & PCIE_GPP_COMPLIANCE)) {
+- u8 res = PcieTrainPort(nb_dev, dev, port);
+- printk(BIOS_DEBUG, "PcieTrainPort port=0x%x result=%d\n", port, res);
++ u8 res = PcieTrainPort(nb_dev, dev, hw_port);
++ printk(BIOS_DEBUG, "PcieTrainPort port=0x%x hw_port=0x%x result=%d\n",
++ port, hw_port, res);
+ if (res) {
+ AtiPcieCfg.PortDetect |= 1 << port;
+ } else {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0060-southbridge-amd-sr5650-Add-optional-delay-after-link.patch b/resources/libreboot/patch/kgpe-d16/0060-southbridge-amd-sr5650-Add-optional-delay-after-link.patch
new file mode 100644
index 00000000..99297539
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0060-southbridge-amd-sr5650-Add-optional-delay-after-link.patch
@@ -0,0 +1,68 @@
+From 542a978ac79f24d04a5afab2084f0f783f35ae58 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 12 Jun 2015 20:08:29 -0500
+Subject: [PATCH 060/146] southbridge/amd/sr5650: Add optional delay after
+ link training
+
+Certain devices (such as the LSI SAS 2008 controller) do not
+respond to PCI probes immediately after link training. If it
+is known that such a device is likely to be installed allow the
+mainboard to insert an appropriate delay.
+---
+ src/southbridge/amd/sr5650/chip.h | 4 ++++
+ src/southbridge/amd/sr5650/sr5650.c | 3 +++
+ 2 files changed, 7 insertions(+)
+
+diff --git a/src/southbridge/amd/sr5650/chip.h b/src/southbridge/amd/sr5650/chip.h
+index 8a68998..d23c614 100644
+--- a/src/southbridge/amd/sr5650/chip.h
++++ b/src/southbridge/amd/sr5650/chip.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -27,6 +28,9 @@ struct southbridge_amd_sr5650_config
+ u8 gpp2_configuration; /* The configuration of General Purpose Port. */
+ u8 gpp3a_configuration; /* The configuration of General Purpose Port. */
+ u16 port_enable; /* Which port is enabled? GPP(2,3,4,5,6,7,9,10,11,12,13) */
++ uint32_t pcie_settling_time; /* How long to wait after link training for PCI-e devices to
++ * initialize before probing PCI-e busses (in microseconds).
++ */
+ };
+
+ #endif /* SR5650_CHIP_H */
+diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
+index 75383de..6db1eb1 100644
+--- a/src/southbridge/amd/sr5650/sr5650.c
++++ b/src/southbridge/amd/sr5650/sr5650.c
+@@ -345,6 +345,7 @@ void sr5650_enable(device_t dev)
+ {
+ device_t nb_dev = 0, sb_dev = 0;
+ int dev_ind;
++ struct southbridge_amd_sr5650_config *cfg;
+
+ printk(BIOS_INFO, "sr5650_enable: dev=%p, VID_DID=0x%x\n", dev, get_vid_did(dev));
+ nb_dev = dev_find_slot(0, PCI_DEVFN(0, 0));
+@@ -352,6 +353,7 @@ void sr5650_enable(device_t dev)
+ die("sr5650_enable: CAN NOT FIND SR5650 DEVICE, HALT!\n");
+ /* NOT REACHED */
+ }
++ cfg = (struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
+
+ /* sb_dev (dev 8) is a bridge that links to southbridge. */
+ sb_dev = dev_find_slot(0, PCI_DEVFN(8, 0));
+@@ -432,6 +434,7 @@ void sr5650_enable(device_t dev)
+ /* Lock HWInit Register after the last device was done */
+ if (dev_ind == 13) {
+ sr56x0_lock_hwinitreg();
++ udelay(cfg->pcie_settling_time);
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0061-mainboard-asus-kgpe-d16-Properly-configure-SR5690-so.patch b/resources/libreboot/patch/kgpe-d16/0061-mainboard-asus-kgpe-d16-Properly-configure-SR5690-so.patch
new file mode 100644
index 00000000..be415902
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0061-mainboard-asus-kgpe-d16-Properly-configure-SR5690-so.patch
@@ -0,0 +1,30 @@
+From 62cde1966c383de9e3a4d579d5201406ee2f82f3 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 12 Jun 2015 20:10:58 -0500
+Subject: [PATCH 061/146] mainboard/asus/kgpe-d16: Properly configure SR5690
+ southbridge PIKE slot
+
+---
+ src/mainboard/asus/kgpe-d16/devicetree.cb | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/devicetree.cb b/src/mainboard/asus/kgpe-d16/devicetree.cb
+index 9bff01e..05b975b 100644
+--- a/src/mainboard/asus/kgpe-d16/devicetree.cb
++++ b/src/mainboard/asus/kgpe-d16/devicetree.cb
+@@ -43,9 +43,9 @@ chip northbridge/amd/amdfam10/root_complex # Root complex
+ end
+ register "gpp1_configuration" = "0" # Configuration 16:0 default
+ register "gpp2_configuration" = "1" # Configuration 8:8
+- #register "gpp3a_configuration" = "2" # Configuration 4:1:1:0:0:0
+- register "gpp3a_configuration" = "11" # Configuration 1:1:1:1:1:1
+- register "port_enable" = "0x3ffc" # Enable all ports except 0 and 1
++ register "gpp3a_configuration" = "2" # Configuration 4:1:1:0:0:0
++ register "port_enable" = "0x3f1c" # Enable all ports except 0, 1, 5, 6, and 7
++ register "pcie_settling_time" = "1000000" # Allow PIKE to be detected / configured
+ end
+ chip southbridge/amd/sb700 # Secondary southbridge
+ device pci 11.0 on end # SATA
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0062-southbridge-amd-sb700-Add-option-to-disable-SATA-ALP.patch b/resources/libreboot/patch/kgpe-d16/0062-southbridge-amd-sb700-Add-option-to-disable-SATA-ALP.patch
new file mode 100644
index 00000000..8bf32201
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0062-southbridge-amd-sb700-Add-option-to-disable-SATA-ALP.patch
@@ -0,0 +1,84 @@
+From 449d225e67724a4f1e0de116f1b37af0a07565fe Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 18 Jun 2015 11:48:02 -0500
+Subject: [PATCH 062/146] southbridge/amd/sb700: Add option to disable SATA
+ ALPM
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 1 +
+ src/mainboard/asus/kgpe-d16/cmos.layout | 8 ++++----
+ src/southbridge/amd/sb700/sata.c | 12 ++++++++++++
+ 3 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 73f2a38..9b30b00 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -18,6 +18,7 @@ interleave_memory_channels = Enable
+ cpu_c_states = Enable
+ cpu_cc6_state = Enable
+ sata_ahci_mode = Enable
++sata_alpm = Disable
+ maximum_p_state_limit = 0xf
+ ieee1394 = Enable
+ power_on_after_fail = On
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index e91568c..f705af2 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -46,11 +46,11 @@ entries
+ 465 1 e 1 cpu_c_states
+ 466 1 e 1 cpu_cc6_state
+ 467 1 e 1 sata_ahci_mode
+-468 4 h 0 maximum_p_state_limit
+-472 2 e 13 dimm_spd_checksum
+-474 1 r 0 allow_spd_nvram_cache_restore
++468 1 e 1 sata_alpm
++469 4 h 0 maximum_p_state_limit
++473 2 e 13 dimm_spd_checksum
++475 1 r 0 allow_spd_nvram_cache_restore
+ 477 1 e 1 ieee1394
+->>>>>>> bed9a97... src/northbridge/amd/amdmct: Add option to override bad SPD checksum
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+ # Reserve the extended AMD configuration registers
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index dda2fc3..10dbca2 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -108,6 +108,7 @@ static void sata_init(struct device *dev)
+ int i, j;
+ uint8_t nvram;
+ uint8_t sata_ahci_mode;
++ uint8_t sata_alpm_enable;
+ uint8_t port_count;
+ uint8_t max_port_count;
+
+@@ -115,6 +116,10 @@ static void sata_init(struct device *dev)
+ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
+ sata_ahci_mode = !!nvram;
+
++ sata_alpm_enable = 0;
++ if (get_option(&nvram, "sata_alpm") == CB_SUCCESS)
++ sata_alpm_enable = !!nvram;
++
+ device_t sm_dev;
+ /* SATA SMBus Disable */
+ sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
+@@ -233,6 +238,13 @@ static void sata_init(struct device *dev)
+ dword &= ~(0x1 << i);
+ write32(sata_bar5 + 0x0c, dword);
+
++ /* Disable ALPM if ALPM support not requested */
++ if (!sata_alpm_enable) {
++ dword = read32(sata_bar5 + 0xfc);
++ dword &= ~(0x1 << 11); /* Disable ALPM */
++ write32(sata_bar5 + 0xfc, dword);
++ }
++
+ /* Write protect Sub-Class Code */
+ byte = pci_read_config8(dev, 0x40);
+ byte &= ~(1 << 0);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0063-mainboard-asus-kgpe-d16-Set-SP5100-subtype.patch b/resources/libreboot/patch/kgpe-d16/0063-mainboard-asus-kgpe-d16-Set-SP5100-subtype.patch
new file mode 100644
index 00000000..011376d0
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0063-mainboard-asus-kgpe-d16-Set-SP5100-subtype.patch
@@ -0,0 +1,24 @@
+From e25111f4814bf9f7dfd08196673b3fe2aa42f2c4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 18 Jun 2015 12:37:08 -0500
+Subject: [PATCH 063/146] mainboard/asus/kgpe-d16: Set SP5100 subtype
+
+---
+ src/mainboard/asus/kgpe-d16/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index 9c359da..761fc93 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -10,6 +10,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
+ select SOUTHBRIDGE_AMD_SR5650
+ select SOUTHBRIDGE_AMD_SB700
+ select SOUTHBRIDGE_AMD_SB700_DISABLE_ISA_DMA
++ select SOUTHBRIDGE_AMD_SUBTYPE_SP5100
+ select SUPERIO_NUVOTON_NCT5572D
+ select PARALLEL_CPU_INIT
+ select HAVE_ROMSTAGE_CONSOLE_SPINLOCK
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0064-northbridge-amd-amdmct-Fix-crash-on-startup-due-to-N.patch b/resources/libreboot/patch/kgpe-d16/0064-northbridge-amd-amdmct-Fix-crash-on-startup-due-to-N.patch
new file mode 100644
index 00000000..401ad06c
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0064-northbridge-amd-amdmct-Fix-crash-on-startup-due-to-N.patch
@@ -0,0 +1,31 @@
+From 05a000dc3aaf0f156962d0247fa464f4bfb065e9 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 20 Jun 2015 14:40:56 -0500
+Subject: [PATCH 064/146] northbridge/amd/amdmct: Fix crash on startup due to
+ NULL pointer access
+
+---
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index d1da07b..2376b20 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -353,11 +353,10 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
+ #endif
+
+ for (i = 0; i < 2; i++) {
+- sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[i];
+ highest_rank_count[i] = 0x0;
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
+- if (pDCTData->DimmRanks[dimm] > highest_rank_count[i])
+- highest_rank_count[i] = pDCTData->DimmRanks[dimm];
++ if (pDCTstat->DimmRanks[dimm] > highest_rank_count[i])
++ highest_rank_count[i] = pDCTstat->DimmRanks[dimm];
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0065-northbridge-amd-amdmct-Clear-memory-before-enabling-.patch b/resources/libreboot/patch/kgpe-d16/0065-northbridge-amd-amdmct-Clear-memory-before-enabling-.patch
new file mode 100644
index 00000000..5ab23210
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0065-northbridge-amd-amdmct-Clear-memory-before-enabling-.patch
@@ -0,0 +1,183 @@
+From 7599efdbd3f19a7d57036a9f6b56da8253fab4ce Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 20 Jun 2015 20:02:49 -0500
+Subject: [PATCH 065/146] northbridge/amd/amdmct: Clear memory before enabling
+ ECC
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 45 ++++++++----------------
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 38 +++++++++++++++++++-
+ 2 files changed, 51 insertions(+), 32 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 28f8d18..e39ce17 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -51,8 +51,6 @@ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+-static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA);
+ static u8 NodePresent_D(u8 Node);
+ static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+@@ -1494,10 +1492,11 @@ restartinit:
+ InterleaveChannels_D(pMCTstat, pDCTstatA);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+- if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+- MCTMemClr_D(pMCTstat,pDCTstatA);
+- }
++ ECCInit_D(pMCTstat, pDCTstatA); /* Setup ECC control and ECC check-bits*/
++
++ /* mctDoWarmResetMemClr_D(); */
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
++ MCTMemClr_D(pMCTstat,pDCTstatA);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -2087,9 +2086,6 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+ mctHookAfterAnyTraining();
+-
+- /* mctDoWarmResetMemClr_D(); */
+- MCTMemClr_D(pMCTstat, pDCTstatA);
+ }
+
+ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+@@ -2377,26 +2373,6 @@ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+-static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA)
+-{
+- /* Ensures that memory clear has completed on all node.*/
+- u8 Node;
+- struct DCTStatStruc *pDCTstat;
+-
+- if (!mctGet_NVbits(NV_DQSTrainCTL)){
+- /* callback to wrapper: mctDoWarmResetMemClr_D */
+- } else { /* NV_DQSTrainCTL == 1 */
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- pDCTstat = pDCTstatA + Node;
+-
+- if (pDCTstat->NodePresent) {
+- DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+- }
+- }
+- }
+-}
+-
+ static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+@@ -2417,9 +2393,12 @@ static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ } while (!(val & (1 << Dr_MemClrStatus)));
+ }
+
+- val = 0x0FE40FC0; /* BKDG recommended */
++ if (is_fam15h())
++ val = 0x0ce00f41; /* BKDG recommended */
++ else
++ val = 0x0fe40fc0; /* BKDG recommended */
+ val |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
+- Set_NB32(dev, 0x11C, val);
++ Set_NB32(dev, 0x11c, val);
+ }
+
+ static u8 NodePresent_D(u8 Node)
+@@ -3082,6 +3061,8 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ u16 proposedFreq;
+ u16 word;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* Get CPU Si Revision defined limit (NPT) */
+ if (is_fam15h())
+ proposedFreq = 933;
+@@ -3106,6 +3087,8 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ pDCTstat->PresetmaxFreq = word;
+ }
+ /* Check F3xE8[DdrMaxRate] for maximum DRAM data rate support */
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index c930380..99cccb8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -88,6 +88,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ u32 val;
+ u16 nvbits;
+
++ uint32_t dword;
++ uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
++ uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
++
+ mctHookBeforeECC();
+
+ /* Construct these booleans, based on setup options, for easy handling
+@@ -111,6 +115,25 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ nvbits = mctGet_NVbits(NV_DramBKScrub);
+ OF_ScrubCTL |= nvbits;
+
++ /* Prevent lockups on DRAM errors during ECC init */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (NodePresent_D(Node)) {
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
++ sync_flood_on_dram_err[Node] = (dword >> 30) & 0x1;
++ sync_flood_on_any_uc_err[Node] = (dword >> 21) & 0x1;
++ dword &= ~(0x1 << 30);
++ dword &= ~(0x1 << 21);
++ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
++
++ /* Clear the RAM before enabling ECC to prevent MCE-related lockups */
++ DCTMemClr_Init_D(pMCTstat, pDCTstat);
++ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
++ }
++ }
++
+ AllECC = 1;
+ MemClrECC = 0;
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -157,7 +180,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ } /* Node has Dram */
+
+ if (MemClrECC) {
+- MCTMemClrSync_D(pMCTstat, pDCTstatA);
++ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+ }
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
+@@ -170,6 +193,19 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ } /* if Node present */
+ }
+
++ /* Restore previous MCA error handling settings */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (NodePresent_D(Node)) {
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
++ dword |= (sync_flood_on_dram_err[Node] & 0x1) << 30;
++ dword |= (sync_flood_on_any_uc_err[Node] & 0x1) << 21;
++ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
++ }
++ }
++
+ if(AllECC)
+ pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
+ else
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0066-southbridge-amd-sb700-Do-drive-detection-even-in-AHC.patch b/resources/libreboot/patch/kgpe-d16/0066-southbridge-amd-sb700-Do-drive-detection-even-in-AHC.patch
new file mode 100644
index 00000000..3c6c1f37
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0066-southbridge-amd-sb700-Do-drive-detection-even-in-AHC.patch
@@ -0,0 +1,140 @@
+From b199e0e69955a8313cee68c1d589cac5956982b9 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 20 Jun 2015 21:31:15 -0500
+Subject: [PATCH 066/146] southbridge/amd/sb700: Do drive detection even in
+ AHCI mode
+
+SeaBIOS AHCI drive detection randomly fails for drives present
+on the secondary channel of each AHCI SATA BAR. Forcing native
+drive detection in AHCI mode resolves this issue.
+---
+ src/southbridge/amd/sb700/sata.c | 101 ++++++++++++++++++++------------------
+ 1 file changed, 54 insertions(+), 47 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index 10dbca2..6afdfdf 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -301,65 +301,72 @@ static void sata_init(struct device *dev)
+ if (port_count > max_port_count)
+ port_count = max_port_count;
+
+- if (!sata_ahci_mode) {
+- /* RPR7.7 SATA drive detection. */
+- /* Use BAR5+0x128,BAR0 for Primary Slave */
+- /* Use BAR5+0x1A8,BAR0 for Primary Slave */
+- /* Use BAR5+0x228,BAR2 for Secondary Master */
+- /* Use BAR5+0x2A8,BAR2 for Secondary Slave */
+- /* Use BAR5+0x328,PATA_BAR0/2 for Primary/Secondary Master emulation */
+- /* Use BAR5+0x3A8,PATA_BAR0/2 for Primary/Secondary Slave emulation */
+- for (i = 0; i < port_count; i++) {
++ /* RPR7.7 SATA drive detection. */
++ /* Use BAR5+0x128,BAR0 for Primary Slave */
++ /* Use BAR5+0x1A8,BAR0 for Primary Slave */
++ /* Use BAR5+0x228,BAR2 for Secondary Master */
++ /* Use BAR5+0x2A8,BAR2 for Secondary Slave */
++ /* Use BAR5+0x328,PATA_BAR0/2 for Primary/Secondary Master emulation */
++ /* Use BAR5+0x3A8,PATA_BAR0/2 for Primary/Secondary Slave emulation */
++ for (i = 0; i < port_count; i++) {
++ byte = read8(sata_bar5 + 0x128 + 0x80 * i);
++ printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
++ byte &= 0xF;
++ if (byte == 0x1) {
++ /* If the drive status is 0x1 then we see it but we aren't talking to it. */
++ /* Try to do something about it. */
++ printk(BIOS_SPEW, "SATA device detected but not talking. Trying lower speed.\n");
++
++ /* Read in Port-N Serial ATA Control Register */
++ byte = read8(sata_bar5 + 0x12C + 0x80 * i);
++
++ /* Set Reset Bit and 1.5g bit */
++ byte |= 0x11;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++
++ /* Clear Reset Bit */
++ byte &= ~0x01;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++
++ /* Reread status */
+ byte = read8(sata_bar5 + 0x128 + 0x80 * i);
+ printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
+ byte &= 0xF;
+- if (byte == 0x1) {
+- /* If the drive status is 0x1 then we see it but we aren't talking to it. */
+- /* Try to do something about it. */
+- printk(BIOS_SPEW, "SATA device detected but not talking. Trying lower speed.\n");
+-
+- /* Read in Port-N Serial ATA Control Register */
+- byte = read8(sata_bar5 + 0x12C + 0x80 * i);
+-
+- /* Set Reset Bit and 1.5g bit */
+- byte |= 0x11;
+- write8((sata_bar5 + 0x12C + 0x80 * i), byte);
+-
+- /* Wait 1ms */
+- mdelay(1);
+-
+- /* Clear Reset Bit */
+- byte &= ~0x01;
+- write8((sata_bar5 + 0x12C + 0x80 * i), byte);
+-
+- /* Wait 1ms */
+- mdelay(1);
+-
+- /* Reread status */
+- byte = read8(sata_bar5 + 0x128 + 0x80 * i);
+- printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte);
+- byte &= 0xF;
++ }
++
++ if (byte == 0x3) {
++ for (j = 0; j < 10; j++) {
++ if (i < 4)
++ current_bar = ((i / 2) == 0) ? sata_bar0 : sata_bar2;
++ else
++ current_bar = ide_bar0;
++ if (!sata_drive_detect(i, current_bar))
++ break;
+ }
+-
+- if (byte == 0x3) {
+- for (j = 0; j < 10; j++) {
+- if (i < 4)
+- current_bar = ((i / 2) == 0) ? sata_bar0 : sata_bar2;
+- else
+- current_bar = ide_bar0;
+- if (!sata_drive_detect(i, current_bar))
+- break;
+- }
++ if (sata_ahci_mode)
++ printk(BIOS_DEBUG, "AHCI device %d is %sready after %i tries\n",
++ i,
++ (j == 10) ? "not " : "",
++ (j == 10) ? j : j + 1);
++ else
+ printk(BIOS_DEBUG, "%s %s device is %sready after %i tries\n",
+ (i / 2) ? "Secondary" : "Primary",
+ (i % 2 ) ? "Slave" : "Master",
+ (j == 10) ? "not " : "",
+ (j == 10) ? j : j + 1);
+- } else {
++ } else {
++ if (sata_ahci_mode)
++ printk(BIOS_DEBUG, "No AHCI SATA drive on Slot%i\n", i);
++ else
+ printk(BIOS_DEBUG, "No %s %s SATA drive on Slot%i\n",
+ (i / 2) ? "Secondary" : "Primary",
+ (i % 2 ) ? "Slave" : "Master", i);
+- }
+ }
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0067-src-southbridge-amd-sb700-Reset-SATA-controller-in-A.patch b/resources/libreboot/patch/kgpe-d16/0067-src-southbridge-amd-sb700-Reset-SATA-controller-in-A.patch
new file mode 100644
index 00000000..350e3d7e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0067-src-southbridge-amd-sb700-Reset-SATA-controller-in-A.patch
@@ -0,0 +1,93 @@
+From 6e707886c395da568608ac70760ce03b00c737c4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 21 Jun 2015 16:27:03 -0500
+Subject: [PATCH 067/146] src/southbridge/amd/sb700: Reset SATA controller in
+ AHCI mode during startup
+
+In AHCI mode SeaBIOS randomly fails to detect disks (AHCI timeouts),
+with the probability of a failure increasing with the number of disks
+connected to the controller. Resetting the SATA controller appears to
+show the true state of the underlying hardware, allowing the drive
+detection code to attempt link renegotiation as needed.
+---
+ src/southbridge/amd/sb700/sata.c | 47 ++++++++++++++++++++++++++++----------
+ 1 file changed, 35 insertions(+), 12 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index 6afdfdf..9f610a4 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -135,6 +135,8 @@ static void sata_init(struct device *dev)
+ /* get rev_id */
+ rev_id = pci_read_config8(sm_dev, 0x08) - 0x28;
+
++ printk(BIOS_SPEW, "rev_id=%x\n", rev_id);
++
+ if (sata_ahci_mode) {
+ /* Enable link latency enhancement on A14 and above */
+ if (rev_id >= 0x14) {
+@@ -245,6 +247,27 @@ static void sata_init(struct device *dev)
+ write32(sata_bar5 + 0xfc, dword);
+ }
+
++ if (sata_ahci_mode) {
++ /* FIXME
++ * SeaBIOS does not know how to spin
++ * up the drives and therefore hangs
++ * in AHCI init if this is enabled...
++ */
++ /* Enable staggered spin-up */
++ dword = read32(sata_bar5 + 0x00);
++#if 0
++ dword |= 0x1 << 27;
++#else
++ dword &= ~(0x1 << 27);
++#endif
++ write32(sata_bar5 + 0x00, dword);
++
++ /* Reset the HBA to avoid stuck drives in SeaBIOS */
++ dword = read32(sata_bar5 + 0x04);
++ dword |= 0x1;
++ write32(sata_bar5 + 0x04, dword);
++ }
++
+ /* Write protect Sub-Class Code */
+ byte = pci_read_config8(dev, 0x40);
+ byte &= ~(1 << 0);
+@@ -371,21 +394,21 @@ static void sata_init(struct device *dev)
+ }
+
+ /* Below is CIM InitSataLateFar */
+- /* Enable interrupts from the HBA */
+- byte = read8(sata_bar5 + 0x4);
+- byte |= 1 << 1;
+- write8((sata_bar5 + 0x4), byte);
+-
+ if (!sata_ahci_mode) {
+- /* Clear error status */
+- write32((sata_bar5 + 0x130), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x1b0), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x230), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x2b0), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x330), 0xFFFFFFFF);
+- write32((sata_bar5 + 0x3b0), 0xFFFFFFFF);
++ /* Enable interrupts from the HBA */
++ byte = read8(sata_bar5 + 0x4);
++ byte |= 1 << 1;
++ write8((sata_bar5 + 0x4), byte);
+ }
+
++ /* Clear error status */
++ write32((sata_bar5 + 0x130), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x1b0), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x230), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x2b0), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x330), 0xFFFFFFFF);
++ write32((sata_bar5 + 0x3b0), 0xFFFFFFFF);
++
+ /* Clear SATA status,Firstly we get the AcpiGpe0BlkAddr */
+ /* ????? why CIM does not set the AcpiGpe0BlkAddr , but use it??? */
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0068-southbridge-amd-sb700-Recover-if-AHCI-disk-detection.patch b/resources/libreboot/patch/kgpe-d16/0068-southbridge-amd-sb700-Recover-if-AHCI-disk-detection.patch
new file mode 100644
index 00000000..dbc48be6
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0068-southbridge-amd-sb700-Recover-if-AHCI-disk-detection.patch
@@ -0,0 +1,161 @@
+From 2c28e8112799a9ec7b72478c0593664dbeab4757 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 22 Jun 2015 02:21:29 -0500
+Subject: [PATCH 068/146] southbridge/amd/sb700: Recover if AHCI disk
+ detection fails
+
+---
+ src/southbridge/amd/sb700/sata.c | 83 ++++++++++++++++++++++++++++++++++----
+ 1 file changed, 75 insertions(+), 8 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index 9f610a4..f27ec49 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -31,12 +31,15 @@
+ static int sata_drive_detect(int portnum, uint16_t iobar)
+ {
+ u8 byte, byte2;
++ u8 byte_prev, byte2_prev;
+ int i = 0;
++ byte_prev = byte2_prev = 0;
+ outb(0xa0 + 0x10 * (portnum % 2), iobar + 0x6);
+ while (byte = inb(iobar + 0x6), byte2 = inb(iobar + 0x7),
+ (byte != (0xa0 + 0x10 * (portnum % 2))) ||
+ ((byte2 & 0x88) != 0)) {
+- printk(BIOS_SPEW, "0x6=%x, 0x7=%x\n", byte, byte2);
++ if ((byte != byte_prev) || (byte2 != byte2_prev))
++ printk(BIOS_SPEW, "0x6=%x, 0x7=%x\n", byte, byte2);
+ if (byte != (0xa0 + 0x10 * (portnum % 2))) {
+ /* This will happen at the first iteration of this loop
+ * if the first SATA port is unpopulated and the
+@@ -45,11 +48,22 @@ static int sata_drive_detect(int portnum, uint16_t iobar)
+ printk(BIOS_DEBUG, "drive no longer selected after %i ms, "
+ "retrying init\n", i * 10);
+ return 1;
+- } else
+- printk(BIOS_SPEW, "drive detection not yet completed, "
+- "waiting...\n");
++ } else {
++ if (i == 0)
++ printk(BIOS_SPEW, "drive detection not yet completed, "
++ "waiting...\n");
++ }
+ mdelay(10);
+ i++;
++ byte_prev = byte;
++ byte2_prev = byte2;
++
++ /* Detect stuck SATA controller and attempt reset */
++ if (i > 1024) {
++ printk(BIOS_DEBUG, "drive detection not done after %i ms, "
++ "resetting HBA and retrying init\n", i * 10);
++ return 2;
++ }
+ }
+ printk(BIOS_SPEW, "drive detection done after %i ms\n", i * 10);
+ return 0;
+@@ -105,12 +119,13 @@ static void sata_init(struct device *dev)
+ uint16_t sata_bar0, sata_bar1, sata_bar2, sata_bar3, sata_bar4;
+ uint16_t ide_bar0, ide_bar1, ide_bar2, ide_bar3;
+ uint16_t current_bar;
+- int i, j;
++ int i, j, ret;
+ uint8_t nvram;
+ uint8_t sata_ahci_mode;
+ uint8_t sata_alpm_enable;
+ uint8_t port_count;
+ uint8_t max_port_count;
++ uint8_t hba_reset_count;
+
+ sata_ahci_mode = 0;
+ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
+@@ -124,14 +139,23 @@ static void sata_init(struct device *dev)
+ /* SATA SMBus Disable */
+ sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
+
++ hba_reset_count = 0;
++
++retry_init:
+ byte = pci_read_config8(sm_dev, 0xad);
+ /* Disable SATA SMBUS */
+- byte |= (1 << 0);
+- /* Enable SATA and power saving */
+ byte |= (1 << 1);
++ /* Enable SATA and power saving */
++ byte |= (1 << 0);
+ byte |= (1 << 5);
+ pci_write_config8(sm_dev, 0xad, byte);
+
++ /* Take the PHY logic out of reset */
++ word = pci_read_config16(dev, 0x84);
++ word |= 0x1 << 2;
++ word &= ~0x1f8;
++ pci_write_config16(dev, 0x84, word);
++
+ /* get rev_id */
+ rev_id = pci_read_config8(sm_dev, 0x08) - 0x28;
+
+@@ -324,6 +348,26 @@ static void sata_init(struct device *dev)
+ if (port_count > max_port_count)
+ port_count = max_port_count;
+
++ /* Send COMRESET to all ports */
++ for (i = 0; i < port_count; i++) {
++ /* Read in Port-N Serial ATA Control Register */
++ byte = read8(sata_bar5 + 0x12C + 0x80 * i);
++
++ /* Set Reset Bit */
++ byte |= 0x1;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++
++ /* Clear Reset Bit */
++ byte &= ~0x01;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
++ }
++
+ /* RPR7.7 SATA drive detection. */
+ /* Use BAR5+0x128,BAR0 for Primary Slave */
+ /* Use BAR5+0x1A8,BAR0 for Primary Slave */
+@@ -369,8 +413,31 @@ static void sata_init(struct device *dev)
+ current_bar = ((i / 2) == 0) ? sata_bar0 : sata_bar2;
+ else
+ current_bar = ide_bar0;
+- if (!sata_drive_detect(i, current_bar))
++ ret = sata_drive_detect(i, current_bar);
++ if (ret == 0) {
+ break;
++ } else if (ret == 2) {
++ /* Reset PHY logic */
++ word = pci_read_config16(dev, 0x84);
++ word &= ~(0x1 << 2);
++ word |= 0x1f8;
++ pci_write_config16(dev, 0x84, word);
++
++ /* Disable SATA controller */
++ byte = pci_read_config8(sm_dev, 0xad);
++ byte &= ~(0x1);
++ pci_write_config8(sm_dev, 0xad, byte);
++
++ mdelay(100);
++
++ /* Retry initialization */
++ hba_reset_count++;
++ if (hba_reset_count < 16)
++ goto retry_init;
++ else
++ printk(BIOS_WARNING, "HBA reset count exceeded, "
++ "continuing but AHCI drives may not function\n");
++ }
+ }
+ if (sata_ahci_mode)
+ printk(BIOS_DEBUG, "AHCI device %d is %sready after %i tries\n",
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0069-southbridge-amd-sb700-Fix-SATA-port-4-5-drive-detect.patch b/resources/libreboot/patch/kgpe-d16/0069-southbridge-amd-sb700-Fix-SATA-port-4-5-drive-detect.patch
new file mode 100644
index 00000000..b515a7ac
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0069-southbridge-amd-sb700-Fix-SATA-port-4-5-drive-detect.patch
@@ -0,0 +1,95 @@
+From 26a5ebad4ca95370319d630f323b5a982cc113c0 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 22 Jun 2015 02:56:10 -0500
+Subject: [PATCH 069/146] southbridge/amd/sb700: Fix SATA port 4/5 drive
+ detection
+
+---
+ src/southbridge/amd/sb700/sata.c | 42 ++++++++++++++++++++++++++++++++++----
+ 1 file changed, 38 insertions(+), 4 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index f27ec49..da6f107 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -126,6 +126,8 @@ static void sata_init(struct device *dev)
+ uint8_t port_count;
+ uint8_t max_port_count;
+ uint8_t hba_reset_count;
++ uint8_t ide_io_enabled;
++ uint8_t ide_legacy_io_enabled;
+
+ sata_ahci_mode = 0;
+ if (get_option(&nvram, "sata_ahci_mode") == CB_SUCCESS)
+@@ -170,15 +172,27 @@ retry_init:
+ }
+ }
+
+- /* Disable combined mode */
++ /* Enable combined mode */
+ byte = pci_read_config8(sm_dev, 0xad);
+- byte &= ~(1 << 3);
++ byte |= (1 << 3);
+ pci_write_config8(sm_dev, 0xad, byte);
+
+ device_t ide_dev;
+ /* IDE Device */
+ ide_dev = dev_find_slot(0, PCI_DEVFN(0x14, 1));
+
++ /* Disable legacy IDE mode (enable PATA_BAR0/2) */
++ byte = pci_read_config8(ide_dev, 0x09);
++ ide_legacy_io_enabled = !(byte & 0x1);
++ byte |= 0x1;
++ pci_write_config8(ide_dev, 0x09, byte);
++
++ /* Enable IDE I/O access (enable PATA_BAR0/2) */
++ byte = pci_read_config8(ide_dev, 0x04);
++ ide_io_enabled = byte & 0x1;
++ byte |= 0x1;
++ pci_write_config8(ide_dev, 0x04, byte);
++
+ /* RPR 7.2 SATA Initialization */
+ /* Set the interrupt Mapping to INTG# */
+ byte = pci_read_config8(sm_dev, 0xaf);
+@@ -425,7 +439,8 @@ retry_init:
+
+ /* Disable SATA controller */
+ byte = pci_read_config8(sm_dev, 0xad);
+- byte &= ~(0x1);
++ byte &= ~(1 << 0);
++ byte &= ~(1 << 3);
+ pci_write_config8(sm_dev, 0xad, byte);
+
+ mdelay(100);
+@@ -460,8 +475,27 @@ retry_init:
+ }
+ }
+
++ /* Restore IDE I/O access */
++ if (!ide_io_enabled) {
++ byte = pci_read_config8(ide_dev, 0x04);
++ byte &= ~0x1;
++ pci_write_config8(ide_dev, 0x04, byte);
++ }
++
++ /* Re-enable legacy IDE mode */
++ if (ide_legacy_io_enabled) {
++ byte = pci_read_config8(ide_dev, 0x09);
++ byte &= ~0x1;
++ pci_write_config8(ide_dev, 0x09, byte);
++ }
++
+ /* Below is CIM InitSataLateFar */
+- if (!sata_ahci_mode) {
++ if (sata_ahci_mode) {
++ /* Disable combined mode */
++ byte = pci_read_config8(sm_dev, 0xad);
++ byte &= ~(1 << 3);
++ pci_write_config8(sm_dev, 0xad, byte);
++ } else {
+ /* Enable interrupts from the HBA */
+ byte = read8(sata_bar5 + 0x4);
+ byte |= 1 << 1;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0070-southbridge-amd-sb700-Fix-random-persistent-SATA-AHC.patch b/resources/libreboot/patch/kgpe-d16/0070-southbridge-amd-sb700-Fix-random-persistent-SATA-AHC.patch
new file mode 100644
index 00000000..536823e2
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0070-southbridge-amd-sb700-Fix-random-persistent-SATA-AHC.patch
@@ -0,0 +1,163 @@
+From ca61ec33fb6c8f3eab823d6fa5415c6775b48192 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 22 Jun 2015 20:57:39 -0500
+Subject: [PATCH 070/146] southbridge/amd/sb700: Fix random persistent SATA
+ AHCI drive detection failure
+
+---
+ src/southbridge/amd/sb700/sata.c | 81 ++++++++++++++++++++------------------
+ 1 file changed, 42 insertions(+), 39 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/sata.c b/src/southbridge/amd/sb700/sata.c
+index da6f107..c3efd4a 100644
+--- a/src/southbridge/amd/sb700/sata.c
++++ b/src/southbridge/amd/sb700/sata.c
+@@ -125,7 +125,6 @@ static void sata_init(struct device *dev)
+ uint8_t sata_alpm_enable;
+ uint8_t port_count;
+ uint8_t max_port_count;
+- uint8_t hba_reset_count;
+ uint8_t ide_io_enabled;
+ uint8_t ide_legacy_io_enabled;
+
+@@ -141,14 +140,20 @@ static void sata_init(struct device *dev)
+ /* SATA SMBus Disable */
+ sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0));
+
+- hba_reset_count = 0;
+-
+-retry_init:
++ /* WARNING
++ * Enabling the SATA link latency enhancement (SMBUS 0xAD bit 5)
++ * causes random persistent drive detection failures until it is cleared,
++ * with the probabability of detection failure rising exponentially with
++ * the number of drives attached to the controller!
++ * This happens on Rev15 H/W.
++ * Do NOT follow the RPR advice; leave this bit set at all times...
++ */
+ byte = pci_read_config8(sm_dev, 0xad);
+ /* Disable SATA SMBUS */
+ byte |= (1 << 1);
+ /* Enable SATA and power saving */
+ byte |= (1 << 0);
++ /* Disable link latency enhancement */
+ byte |= (1 << 5);
+ pci_write_config8(sm_dev, 0xad, byte);
+
+@@ -163,15 +168,6 @@ retry_init:
+
+ printk(BIOS_SPEW, "rev_id=%x\n", rev_id);
+
+- if (sata_ahci_mode) {
+- /* Enable link latency enhancement on A14 and above */
+- if (rev_id >= 0x14) {
+- byte = pci_read_config8(sm_dev, 0xad);
+- byte &= ~(1 << 5);
+- pci_write_config8(sm_dev, 0xad, byte);
+- }
+- }
+-
+ /* Enable combined mode */
+ byte = pci_read_config8(sm_dev, 0xad);
+ byte |= (1 << 3);
+@@ -285,6 +281,17 @@ retry_init:
+ write32(sata_bar5 + 0xfc, dword);
+ }
+
++ /* Enable SATA ports */
++ byte = pci_read_config8(dev, 0x42);
++ if (max_port_count <= 6) {
++ byte |= 0x3f;
++ for (i = 0; i < max_port_count; i++)
++ byte &= ~(0x1 << i);
++ } else {
++ byte &= ~0x3f;
++ }
++ pci_write_config8(dev, 0x42, byte);
++
+ if (sata_ahci_mode) {
+ /* FIXME
+ * SeaBIOS does not know how to spin
+@@ -306,6 +313,9 @@ retry_init:
+ write32(sata_bar5 + 0x04, dword);
+ }
+
++ sb7xx_51xx_setup_sata_phys(dev);
++ sb7xx_51xx_setup_sata_port_indication(sata_bar5);
++
+ /* Write protect Sub-Class Code */
+ byte = pci_read_config8(dev, 0x40);
+ byte &= ~(1 << 0);
+@@ -331,7 +341,7 @@ retry_init:
+ else {
+ dword &= ~(1 << 24 | 1 << 21); /* A14 and above */
+ dword &= ~0xFF80; /* 15:7 */
+- dword |= 1 << 15 | 0x7F << 7;
++ dword |= 1 << 15 | 0x7F << 7 | 1 << 6;
+ }
+ pci_write_config32(dev, 0x48, dword);
+
+@@ -339,9 +349,6 @@ retry_init:
+ byte = 0x10;
+ pci_write_config8(dev, 0x46, byte);
+
+- sb7xx_51xx_setup_sata_phys(dev);
+- sb7xx_51xx_setup_sata_port_indication(sata_bar5);
+-
+ /* Enable the I/O, MM, BusMaster access for SATA */
+ byte = pci_read_config8(dev, 0x4);
+ byte |= 7 << 0;
+@@ -426,32 +433,28 @@ retry_init:
+ if (i < 4)
+ current_bar = ((i / 2) == 0) ? sata_bar0 : sata_bar2;
+ else
+- current_bar = ide_bar0;
++ current_bar = (pci_read_config8(sm_dev, 0xad) & (0x1 << 4))
++ ? ide_bar2 : ide_bar0;
+ ret = sata_drive_detect(i, current_bar);
+ if (ret == 0) {
+ break;
+ } else if (ret == 2) {
+- /* Reset PHY logic */
+- word = pci_read_config16(dev, 0x84);
+- word &= ~(0x1 << 2);
+- word |= 0x1f8;
+- pci_write_config16(dev, 0x84, word);
+-
+- /* Disable SATA controller */
+- byte = pci_read_config8(sm_dev, 0xad);
+- byte &= ~(1 << 0);
+- byte &= ~(1 << 3);
+- pci_write_config8(sm_dev, 0xad, byte);
+-
+- mdelay(100);
+-
+- /* Retry initialization */
+- hba_reset_count++;
+- if (hba_reset_count < 16)
+- goto retry_init;
+- else
+- printk(BIOS_WARNING, "HBA reset count exceeded, "
+- "continuing but AHCI drives may not function\n");
++ /* Read in Port-N Serial ATA Control Register */
++ byte = read8(sata_bar5 + 0x12C + 0x80 * i);
++
++ /* Set Reset Bit */
++ byte |= 0x1;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1000ms */
++ mdelay(1000);
++
++ /* Clear Reset Bit */
++ byte &= ~0x01;
++ write8((sata_bar5 + 0x12C + 0x80 * i), byte);
++
++ /* Wait 1ms */
++ mdelay(1);
+ }
+ }
+ if (sata_ahci_mode)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0071-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch b/resources/libreboot/patch/kgpe-d16/0071-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch
new file mode 100644
index 00000000..f37c380e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0071-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch
@@ -0,0 +1,423 @@
+From 4818de5c4895e3de9bca7d7f3566492e8e529e21 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Wed, 24 Jun 2015 19:15:09 -0500
+Subject: [PATCH 071/146] northbridge/amd/amdmct/mct_ddr3: Fix lockups and
+ wasted time during ECC init
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 146 +++++++++++++++---------
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 39 ++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 22 +++-
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 3 +-
+ 4 files changed, 147 insertions(+), 63 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index e39ce17..b66b328 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -983,7 +983,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x00390039;
+@@ -1072,7 +1072,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
++
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa)) {
+ /* DDR3-667 - DDR3-1066 */
+@@ -1492,11 +1492,12 @@ restartinit:
+ InterleaveChannels_D(pMCTstat, pDCTstatA);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+- ECCInit_D(pMCTstat, pDCTstatA); /* Setup ECC control and ECC check-bits*/
+-
+- /* mctDoWarmResetMemClr_D(); */
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+- MCTMemClr_D(pMCTstat,pDCTstatA);
++ if (!ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
++ /* Memory was not cleared during ECC setup */
++ /* mctDoWarmResetMemClr_D(); */
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
++ MCTMemClr_D(pMCTstat,pDCTstatA);
++ }
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -1689,7 +1690,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ uint8_t x8_present = 0;
+ uint8_t memclk_index;
+ uint8_t interleave_channels = 0;
+- uint8_t redirect_ecc_scrub = 0;
+ uint16_t trdrdsddc;
+ uint16_t trdrddd;
+ uint16_t cdd_trdrddd;
+@@ -1727,9 +1727,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged))
+ interleave_channels = 1;
+
+- if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+- redirect_ecc_scrub = 1;
+-
+ dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf;
+ if (dword > 6)
+ read_odt_delay = dword - 6;
+@@ -1922,21 +1919,10 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword |= (interleave_channels & 0x1) << 2;
+ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */
+
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */
+- dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
+- dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+- dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
+- dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
+- dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
+- dword |= redirect_ecc_scrub & 0x1;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
+- dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++ /* NOTE
++ * ECC-related setup is performed as part of ECCInit_D and must not be located here,
++ * otherwise semi-random lockups will occur due to misconfigured scrubbing hardware!
++ */
+
+ /* FIXME
+ * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
+@@ -1978,11 +1964,17 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
+ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */
+
++ /* Configure partial power down delay */
++ dword = Get_NB32(dev, 0x244); /* DRAM Controller Miscellaneous 3 */
++ dword &= ~0xf; /* PrtlChPDDynDly = 0x2 */
++ dword |= 0x2;
++ Set_NB32(dev, 0x244, dword); /* DRAM Controller Miscellaneous 3 */
++
+ /* Enable prefetchers */
+- dword = Get_NB32_DCT(dev, dct, 0x110); /* Memory Controller Configuration High */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
+ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
+ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
+- Set_NB32_DCT(dev, dct, 0x110, dword); /* Memory Controller Configuration High */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
+ }
+ }
+
+@@ -2084,6 +2076,19 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
+ }
+
++ if (is_fam15h()) {
++ uint8_t Node;
++ struct DCTStatStruc *pDCTstat;
++
++ /* Switch DCT control register to DCT 0 per Erratum 505 */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ pDCTstat = pDCTstatA + Node;
++ if (pDCTstat->NodePresent) {
++ fam15h_switch_dct(pDCTstat->dev_map, 0);
++ }
++ }
++ }
++
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+ mctHookAfterAnyTraining();
+ }
+@@ -2330,6 +2335,7 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ * status are checked to ensure that memclr has completed.
+ */
+ u8 Node;
++ uint32_t dword;
+ struct DCTStatStruc *pDCTstat;
+
+ if (!mctGet_NVbits(NV_DQSTrainCTL)){
+@@ -2350,6 +2356,16 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+ }
++
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ /* Configure and enable prefetchers */
++ if (is_fam15h())
++ dword = 0x0ce00f41; /* BKDG recommended */
++ else
++ dword = 0x0fe40fc0; /* BKDG recommended */
++ dword |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, dword);
++ }
+ }
+
+ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+@@ -2357,48 +2373,59 @@ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+ u32 dev;
+- u32 reg;
++ uint32_t dword;
+
+ /* Initiates a memory clear operation on one node */
+ if (pDCTstat->DCTSysLimit) {
+ dev = pDCTstat->dev_dct;
+- reg = 0x110;
++
++ /* Disable prefetchers */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
++ dword |= 0x1 << 13; /* PrefIoDis = 1 */
++ dword |= 0x1 << 12; /* PrefCpuDis = 1 */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
+
+ do {
+- val = Get_NB32(dev, reg);
++ val = Get_NB32(dev, 0x110);
+ } while (val & (1 << MemClrBusy));
+
+ val |= (1 << MemClrInit);
+- Set_NB32(dev, reg, val);
++ Set_NB32(dev, 0x110, val);
+ }
+ }
+
+ static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+- u32 val;
+- u32 dev = pDCTstat->dev_dct;
+- u32 reg;
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+ /* Ensure that a memory clear operation has completed on one node */
+ if (pDCTstat->DCTSysLimit){
+- reg = 0x110;
+-
++ printk(BIOS_DEBUG, "%s: Waiting for memory clear to complete", __func__);
+ do {
+- val = Get_NB32(dev, reg);
+- } while (val & (1 << MemClrBusy));
++ dword = Get_NB32(dev, 0x110);
++
++ printk(BIOS_DEBUG, ".");
++ } while (dword & (1 << MemClrBusy));
+
++ printk(BIOS_DEBUG, "\n");
+ do {
+- val = Get_NB32(dev, reg);
+- } while (!(val & (1 << Dr_MemClrStatus)));
++ printk(BIOS_DEBUG, ".");
++ dword = Get_NB32(dev, 0x110);
++ } while (!(dword & (1 << Dr_MemClrStatus)));
++ printk(BIOS_DEBUG, "\n");
+ }
+
+- if (is_fam15h())
+- val = 0x0ce00f41; /* BKDG recommended */
+- else
+- val = 0x0fe40fc0; /* BKDG recommended */
+- val |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
+- Set_NB32(dev, 0x11c, val);
++ /* Enable prefetchers */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
++ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
++ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u8 NodePresent_D(u8 Node)
+@@ -3339,8 +3366,8 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ dev = pDCTstat->dev_dct;
+
+ /* Build Dram Control Register Value */
+- DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8); /* Dram Control*/
+- DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control*/
++ DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xa8); /* Dram Miscellaneous 2 */
++ DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control */
+
+ /* FIXME: Skip mct_checkForDxSupport */
+ /* REV_CALL mct_DoRdPtrInit if not Dx */
+@@ -3395,9 +3422,15 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ /* set only if x8 Registered DIMMs in System*/
+ DramConfigHi |= 1 << RDqsEn;
+
+- if (mctGet_NVbits(NV_CKE_CTL))
+- /*Chip Select control of CKE*/
+- DramConfigHi |= 1 << 16;
++ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
++ DramConfigLo |= 1 << 25; /* PendRefPaybackS3En = 1 */
++ DramConfigLo |= 1 << 24; /* StagRefEn = 1 */
++ DramConfigHi |= 1 << 16; /* PowerDownMode = 1 */
++ } else {
++ if (mctGet_NVbits(NV_CKE_CTL))
++ /*Chip Select control of CKE*/
++ DramConfigHi |= 1 << 16;
++ }
+
+ /* Control Bank Swizzle */
+ if (0) /* call back not needed mctBankSwizzleControl_D()) */
+@@ -4105,8 +4138,7 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+ #if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
+ if (load_spd_hashes_from_nvram(pMCTstat, pDCTstat) < 0) {
+ pDCTstat->spd_data.nvram_spd_match = 0;
+- }
+- else {
++ } else {
+ compare_nvram_spd_hashes(pMCTstat, pDCTstat);
+ }
+ #else
+@@ -4300,8 +4332,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ }
+ for (i=i_start; i<i_end; i++) {
+ index_reg = 0x98;
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A/B Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A/B Output Driver Compensation Control */
+ }
+
+ return pDCTstat->ErrCode;
+@@ -6091,11 +6123,11 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ DramMRS |= 1 << 1;
+
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
++ dword |= DramMRS;
+ if (is_fam15h())
+ dword &= ~0x00800003;
+ else
+ dword &= ~0x00fc2f8f;
+- dword |= DramMRS;
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index 99cccb8..e352886 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -92,8 +92,13 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
+ uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
+
++ uint8_t redirect_ecc_scrub = 0;
++
+ mctHookBeforeECC();
+
++ if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
++ redirect_ecc_scrub = 1;
++
+ /* Construct these booleans, based on setup options, for easy handling
+ later in this procedure */
+ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
+@@ -230,12 +235,12 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+- if(OB_ECCRedir) {
+- val |= (1<<0); /* enable redirection */
++ if (OB_ECCRedir) {
++ val |= (1<<0); /* enable redirection */
+ }
+- Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
++ Set_NB32(dev, 0x5c, val); /* Dram Scrub Addr Low */
+ val = curBase>>24;
+- Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
++ Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+ Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
+
+ if (!is_fam15h()) {
+@@ -252,6 +257,32 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ }
+ }
++
++ if (is_fam15h()) {
++ uint8_t dct;
++
++ /* Disable training mode
++ * See fam15EnableTrainingMode for the non-ECC training mode tear-down code
++ */
++ for (dct = 0; dct < 2; dct++) {
++ /* NOTE: Reads use DCT 0 and writes use the current DCT per Erratum 505 */
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, 0, 0x58); /* Scrub Rate Control */
++ dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
++ dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
++ dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
++ dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
++ dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
++ dword |= redirect_ecc_scrub & 0x1;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
++ dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++ }
++ }
+ } /* this node has ECC enabled dram */
+ } /*Node has Dram */
+ } /*if Node present */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index 5ef4a2c..32b447f 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -23,7 +23,27 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+ {
+ u32 val;
+
+- if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
++ uint8_t cs_mux_45;
++ uint8_t cs_mux_67;
++
++ /* BKDG v3.14 Table 200 / Table 201 */
++ if (MaxDimmsInstallable < 3) {
++ cs_mux_45 = 1;
++ cs_mux_67 = 1;
++ } else {
++ cs_mux_45 = 0;
++ cs_mux_67 = 0;
++ }
++ misc2 |= (cs_mux_45 & 0x1) << 26;
++ misc2 |= (cs_mux_67 & 0x1) << 27;
++ } else if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ misc2 |= 1 << SubMemclkRegDly;
+ if (mctGet_NVbits(NV_MAX_DIMMS) == 8)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index cbb34dc..3995a70 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -209,7 +209,8 @@ void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
+ uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f);
+ uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
+ if (abs(total_delay_phy - total_delay_seed) > 0x20) {
+- printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__);
++ printk(BIOS_DEBUG, "%s: overriding faulty phy value (seed: %04x phy: %04x step: %04x)\n", __func__,
++ total_delay_seed, total_delay_phy, abs(total_delay_phy - total_delay_seed));
+ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
+ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0072-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch b/resources/libreboot/patch/kgpe-d16/0072-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
new file mode 100644
index 00000000..f8392d91
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0072-cpu-amd-Fix-AMD-Family-15h-ECC-initialization-reliab.patch
@@ -0,0 +1,479 @@
+From 1171e5a4e83f2aedeb59b78ffa29cac927ff3441 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 15:07:34 -0500
+Subject: [PATCH 072/146] cpu/amd: Fix AMD Family 15h ECC initialization
+ reliability issues
+
+---
+ src/cpu/amd/car/cache_as_ram.inc | 4 +
+ src/cpu/amd/model_10xxx/init_cpus.c | 16 ++++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 16 ++--
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 21 ++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 110 +++++++++++-------------
+ src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c | 6 +-
+ src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 57 +++++++-----
+ 8 files changed, 138 insertions(+), 98 deletions(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 9c71270..9a51e3c 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -362,12 +362,16 @@ clear_fixed_var_mtrr_out:
+ simplemask CacheSize, 0
+ wrmsr
+
++ jmp_if_fam15h(fam15_skip_dram_mtrr_setup)
++
+ /* Enable memory access for first MBs using top_mem. */
+ movl $TOP_MEM, %ecx
+ xorl %edx, %edx
+ movl $(((CONFIG_RAMTOP) + TOP_MEM_MASK) & ~TOP_MEM_MASK) , %eax
+ wrmsr
+
++fam15_skip_dram_mtrr_setup:
++
+ #if CONFIG_XIP_ROM_SIZE
+
+ /* Enable write base caching so we can do execute in place (XIP)
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 57b9b89..0d1f043 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -317,6 +317,22 @@ static void STOP_CAR_AND_CPU(uint8_t skip_sharedc_config, uint32_t apicid)
+ msr = rdmsr(BU_CFG2);
+ msr.lo &= ~(1 << ClLinesToNbDis);
+ wrmsr(BU_CFG2, msr);
++ } else {
++ /* Family 15h or later
++ * DRAM setup is delayed on Fam15 in order to prevent
++ * any DRAM access before ECC check bits are initialized.
++ * Each core also needs to have its initial DRAM map initialized
++ * before it is put to sleep, otherwise it will fail to wake
++ * in ramstage. To meet both of these goals, delay DRAM map
++ * setup until the last possible moment, where speculative
++ * memory access is highly unlikely before core halt...
++ */
++ if (!skip_sharedc_config) {
++ /* Enable memory access for first MBs using top_mem */
++ msr.hi = 0;
++ msr.lo = (CONFIG_RAMTOP + TOP_MEM_MASK) & (~TOP_MEM_MASK);
++ wrmsr(TOP_MEM, msr);
++ }
+ }
+
+ disable_cache_as_ram(skip_sharedc_config); // inline
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index b66b328..5a0c746 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1447,13 +1447,12 @@ restartinit:
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+-
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n");
+ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
+ mctHookAfterHTMap();
+-
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
+- CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctHookAfterCPU\n");
+ mctHookAfterCPU(); /* Setup external northbridge(s) */
+
+ /* FIXME
+@@ -1476,9 +1475,6 @@ restartinit:
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+ DQSTiming_D(pMCTstat, pDCTstatA, allow_config_restore); /* Get Receiver Enable and DQS signal timing*/
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+- UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
+-
+ if (!allow_config_restore) {
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+ mct_OtherTiming(pMCTstat, pDCTstatA);
+@@ -1499,6 +1495,12 @@ restartinit:
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ }
+
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 11555ae..ac8c934 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -725,8 +725,10 @@ struct amd_s3_persistent_mct_channel_data {
+ uint32_t f2x9cx30[12];
+ uint32_t f2x9cx40[12];
+
+- /* Other (1 dword) */
++ /* Other (3 dwords) */
+ uint32_t f3x58;
++ uint32_t f3x5c;
++ uint32_t f3x60;
+
+ /* Family 15h-specific registers (90 dwords) */
+ uint32_t f2x200;
+@@ -785,7 +787,7 @@ struct amd_s3_persistent_mct_channel_data {
+ uint32_t f2x9cx0d0f0_0_f_31[9]; /* [lane] */
+ uint32_t f2x9cx0d0f8021;
+
+- /* TOTAL: 340 dwords */
++ /* TOTAL: 342 dwords */
+ } __attribute__((packed));
+
+ struct amd_s3_persistent_node_data {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index e74545b..207a135 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -902,6 +902,16 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint32_t dev = pDCTstat->dev_dct;
+ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++#endif
++
++ mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
++ if (fam15h_freq_tab[mem_clk] == 0) {
++ pDCTstat->CH_MaxRdLat[dct] = 0x55;
++ return;
++ }
++
+ /* P is specified in PhyCLKs (1/2 MEMCLKs) */
+ for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) {
+ /* 2.10.5.8.5 (2) */
+@@ -949,7 +959,6 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ t += 800;
+
+ /* 2.10.5.8.5 (10) */
+- mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
+ dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4))); /* Retrieve NbDid, NbFid */
+ nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1);
+ n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
+@@ -964,8 +973,16 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
+
+ /* Save result for later use */
+- pDCTstat->CH_MaxRdLat[dct] = n;
++ pDCTstat->CH_MaxRdLat[dct] = n - 1;
++
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: CH_MaxRdLat[%d]: %03x\n", __func__, dct, pDCTstat->CH_MaxRdLat[dct]);
++#endif
+ }
++
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
++#endif
+ }
+
+ static void start_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index e352886..37db37c 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -92,13 +92,8 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
+ uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
+
+- uint8_t redirect_ecc_scrub = 0;
+-
+ mctHookBeforeECC();
+
+- if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+- redirect_ecc_scrub = 1;
+-
+ /* Construct these booleans, based on setup options, for easy handling
+ later in this procedure */
+ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
+@@ -117,8 +112,11 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ OF_ScrubCTL |= (u32) nvbits << 8;
+ }
+
++ nvbits = mctGet_NVbits(NV_L3BKScrub);
++ OF_ScrubCTL |= (nvbits & 0x1f) << 24; /* L3Scrub = NV_L3BKScrub */
++
+ nvbits = mctGet_NVbits(NV_DramBKScrub);
+- OF_ScrubCTL |= nvbits;
++ OF_ScrubCTL |= nvbits; /* DramScrub = NV_DramBKScrub */
+
+ /* Prevent lockups on DRAM errors during ECC init */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -133,6 +131,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ dword &= ~(0x1 << 21);
+ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
+
++ /* Clear MC4 error status */
++ pci_write_config32(pDCTstat->dev_nbmisc, 0x48, 0x0);
++ pci_write_config32(pDCTstat->dev_nbmisc, 0x4c, 0x0);
++
+ /* Clear the RAM before enabling ECC to prevent MCE-related lockups */
+ DCTMemClr_Init_D(pMCTstat, pDCTstat);
+ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+@@ -170,6 +172,9 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ if(LDramECC) { /* if ECC is enabled on this dram */
+ if (OB_NBECC) {
+ mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
++ val = Get_NB32(pDCTstat->dev_dct, 0x110);
++ val |= 1 << 5; /* DctDatIntLv = 1 */
++ Set_NB32(pDCTstat->dev_dct, 0x110, val);
+ dev = pDCTstat->dev_nbmisc;
+ reg = 0x44; /* MCA NB Configuration */
+ val = Get_NB32(dev, reg);
+@@ -180,37 +185,16 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ printk(BIOS_DEBUG, " ECC enabled on node: %02x\n", Node);
+ }
+ } /* this node has ECC enabled dram */
++
++ if (MemClrECC) {
++ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
++ }
+ } else {
+ LDramECC = 0;
+ } /* Node has Dram */
+-
+- if (MemClrECC) {
+- DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+- }
+-
+- if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
+- /* Set up message triggered C1E */
+- val = pci_read_config32(pDCTstat->dev_nbmisc, 0xd4);
+- val &= ~(0x1 << 15); /* StutterScrubEn = DRAM scrub enabled */
+- val |= (mctGet_NVbits(NV_DramBKScrub)?1:0) << 15;
+- pci_write_config32(pDCTstat->dev_nbmisc, 0xd4, val);
+- }
+ } /* if Node present */
+ }
+
+- /* Restore previous MCA error handling settings */
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
+-
+- if (NodePresent_D(Node)) {
+- dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
+- dword |= (sync_flood_on_dram_err[Node] & 0x1) << 30;
+- dword |= (sync_flood_on_any_uc_err[Node] & 0x1) << 21;
+- Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
+- }
+- }
+-
+ if(AllECC)
+ pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
+ else
+@@ -229,19 +213,26 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ /*WE/RE is checked because memory config may have been */
+ if((val & 3)==3) { /* Node has dram populated */
+ if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
+- if (is_fam15h()) {
+- /* Erratum 505 */
+- fam15h_switch_dct(pDCTstat->dev_map, 0);
+- }
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+ if (OB_ECCRedir) {
+- val |= (1<<0); /* enable redirection */
++ val |= (1 << 0); /* Enable redirection */
+ }
+ Set_NB32(dev, 0x5c, val); /* Dram Scrub Addr Low */
+- val = curBase>>24;
++ val = curBase >> 24;
+ Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+- Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
++
++ /* Set scrub rate controls */
++ if (is_fam15h()) {
++ /* Erratum 505 */
++ fam15h_switch_dct(pDCTstat->dev_map, 0);
++ }
++ Set_NB32(dev, 0x58, OF_ScrubCTL); /* Scrub Control */
++ if (is_fam15h()) {
++ fam15h_switch_dct(pDCTstat->dev_map, 1); /* Erratum 505 */
++ Set_NB32(dev, 0x58, OF_ScrubCTL); /* Scrub Control */
++ fam15h_switch_dct(pDCTstat->dev_map, 0); /* Erratum 505 */
++ }
+
+ if (!is_fam15h()) {
+ /* Divisor should not be set deeper than
+@@ -258,36 +249,31 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ }
+
+- if (is_fam15h()) {
+- uint8_t dct;
+-
+- /* Disable training mode
+- * See fam15EnableTrainingMode for the non-ECC training mode tear-down code
+- */
+- for (dct = 0; dct < 2; dct++) {
+- /* NOTE: Reads use DCT 0 and writes use the current DCT per Erratum 505 */
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, 0, 0x58); /* Scrub Rate Control */
+- dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
+- dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+- dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
+- dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
+- dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
+- dword |= redirect_ecc_scrub & 0x1;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
+- dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
+- }
++ if (pDCTstat->LogicalCPUID & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
++ /* Set up message triggered C1E */
++ val = pci_read_config32(pDCTstat->dev_nbmisc, 0xd4);
++ val &= ~(0x1 << 15); /* StutterScrubEn = DRAM scrub enabled */
++ val |= (mctGet_NVbits(NV_DramBKScrub)?1:0) << 15;
++ pci_write_config32(pDCTstat->dev_nbmisc, 0xd4, val);
+ }
+ } /* this node has ECC enabled dram */
+ } /*Node has Dram */
+ } /*if Node present */
+ }
+
++ /* Restore previous MCA error handling settings */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
++ if (NodePresent_D(Node)) {
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0x44);
++ dword |= (sync_flood_on_dram_err[Node] & 0x1) << 30;
++ dword |= (sync_flood_on_any_uc_err[Node] & 0x1) << 21;
++ Set_NB32(pDCTstat->dev_nbmisc, 0x44, dword);
++ }
++ }
++
+ if(mctGet_NVbits(NV_SyncOnUnEccEn))
+ setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+index 596fb23..abc8ae3 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
+@@ -232,9 +232,9 @@ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat
+ Cache32bTOP = val;
+ pMCTstat->Sub4GCacheTop = val;
+
+- /*======================================================================
+- * Clear variable MTRR values
+- *======================================================================*/
++ /*======================================================================
++ * Clear variable MTRR values
++ *======================================================================*/
+ addr = 0x200;
+ lo = 0;
+ hi = lo;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+index 2132648..2edb7ce 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+@@ -89,6 +89,28 @@ static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint3
+ return pci_read_config32(dev, reg);
+ }
+
++static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
++ if (is_fam15h()) {
++ uint32_t dword;
++#ifdef __PRE_RAM__
++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
++#else
++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
++#endif
++
++ /* Select DCT */
++ dword = pci_read_config32(dev_fn1, 0x10c);
++ dword &= ~0x1;
++ dword |= (dct & 0x1);
++ pci_write_config32(dev_fn1, 0x10c, dword);
++ } else {
++ /* Apply offset */
++ reg += dct * 0x100;
++ }
++
++ pci_write_config32(dev, reg, value);
++}
++
+ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
+ {
+ uint32_t dword;
+@@ -489,29 +511,17 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
+
+ /* Other */
+ /* ECC scrub rate control */
+- data->f3x58 = pci_read_config32(dev_fn3, 0x58);
++ data->f3x58 = read_config32_dct(dev_fn3, node, 0, 0x58);
++
++ /* ECC scrub location */
++ write_config32_dct(dev_fn3, node, 0, 0x58, 0x0); /* Disable sequential scrub to work around non-atomic location read */
++ data->f3x5c = read_config32_dct(dev_fn3, node, 0, 0x5c);
++ data->f3x60 = read_config32_dct(dev_fn3, node, 0, 0x60);
++ write_config32_dct(dev_fn3, node, 0, 0x58, data->f3x58); /* Re-enable sequential scrub */
+ }
+ }
+ }
+ #else
+-static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
+- if (is_fam15h()) {
+- uint32_t dword;
+- device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+-
+- /* Select DCT */
+- dword = pci_read_config32(dev_fn1, 0x10c);
+- dword &= ~0x1;
+- dword |= (dct & 0x1);
+- pci_write_config32(dev_fn1, 0x10c, dword);
+- } else {
+- /* Apply offset */
+- reg += dct * 0x100;
+- }
+-
+- pci_write_config32(dev, reg, value);
+-}
+-
+ static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) {
+ uint32_t dword;
+ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+@@ -613,8 +623,7 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (is_fam15h()) {
+ for (i=0; i<4; i++)
+ write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
+- }
+- else {
++ } else {
+ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
+ }
+
+@@ -1060,8 +1069,12 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
+ if (!persistent_data->node[node].node_present)
+ continue;
+
++ /* ECC scrub location */
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x5c, data->f3x5c);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x60, data->f3x60);
++
+ /* ECC scrub rate control */
+- pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58);
++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x58, data->f3x58);
+
+ if (is_fam15h())
+ /* Set LockDramCfg and CC6SaveEn */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0073-northbridge-amd-amdfam10-Properly-indicate-node-and-.patch b/resources/libreboot/patch/kgpe-d16/0073-northbridge-amd-amdfam10-Properly-indicate-node-and-.patch
new file mode 100644
index 00000000..6db95dad
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0073-northbridge-amd-amdfam10-Properly-indicate-node-and-.patch
@@ -0,0 +1,75 @@
+From c05d2f8115f94ba223ee481bc8790312d8b9fbd5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 15:28:23 -0500
+Subject: [PATCH 073/146] northbridge/amd/amdfam10: Properly indicate node and
+ channel in SMBIOS tables
+
+---
+ src/northbridge/amd/amdfam10/northbridge.c | 7 ++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 12 ++++++++++++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 8 +++++---
+ 3 files changed, 23 insertions(+), 4 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index f777d02..7cbb732 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -1205,7 +1205,12 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
+ t->attributes = 0;
+ t->attributes |= ranks & 0xf; /* rank number is stored in the lowest 4 bits of the attributes field */
+ t->form_factor = MEMORY_FORMFACTOR_DIMM;
+- snprintf(string_buffer, sizeof (string_buffer), "NODE %d DIMM_%s%d", node, (slot & 0x1)?"B":"A", (slot >> 1) + 1);
++ if (mem_info->dct_stat[node].Dual_Node_Package) {
++ snprintf(string_buffer, sizeof (string_buffer), "NODE %d DIMM_%s%d", node >> 1,
++ (mem_info->dct_stat[node].Internal_Node_ID)?((slot & 0x1)?"D":"C"):((slot & 0x1)?"B":"A"), (slot >> 1) + 1);
++ } else {
++ snprintf(string_buffer, sizeof (string_buffer), "NODE %d DIMM_%s%d", node, (slot & 0x1)?"B":"A", (slot >> 1) + 1);
++ }
+ t->device_locator = smbios_add_string(t->eos, string_buffer);
+ if (IS_ENABLED(CONFIG_DIMM_DDR2))
+ t->memory_type = MEMORY_TYPE_DDR2;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 5a0c746..4a47ee8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1385,6 +1385,18 @@ restartinit:
+ pDCTstat->dev_nbctl = PA_NBCTL(Node);
+ pDCTstat->NodeSysBase = node_sys_base;
+
++ if (mctGet_NVbits(NV_PACK_TYPE) == PT_GR) {
++ uint32_t dword;
++ pDCTstat->Dual_Node_Package = 1;
++
++ /* Get the internal node number */
++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0xe8);
++ dword = (dword >> 30) & 0x3;
++ pDCTstat->Internal_Node_ID = dword;
++ } else {
++ pDCTstat->Dual_Node_Package = 0;
++ }
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
+ mct_init(pMCTstat, pDCTstat);
+ mctNodeIDDebugPort_D();
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index ac8c934..8c9da47 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -335,9 +335,11 @@ struct amd_spd_node_data {
+
+ struct DCTStatStruc { /* A per Node structure*/
+ /* DCTStatStruct_F - start */
+- u8 Node_ID; /* Node ID of current controller */
+- uint8_t stopDCT; /* Set if the DCT will be stopped */
+- u8 ErrCode; /* Current error condition of Node
++ u8 Node_ID; /* Node ID of current controller */
++ uint8_t Internal_Node_ID; /* Internal Node ID of the current controller */
++ uint8_t Dual_Node_Package; /* 1=Dual node package (G34) */
++ uint8_t stopDCT; /* Set if the DCT will be stopped */
++ u8 ErrCode; /* Current error condition of Node
+ 0= no error
+ 1= Variance Error, DCT is running but not in an optimal configuration.
+ 2= Stop Error, DCT is NOT running
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0074-amd-amdmct-mct_ddr3-Add-Family-15h-RDIMM-timing-and-.patch b/resources/libreboot/patch/kgpe-d16/0074-amd-amdmct-mct_ddr3-Add-Family-15h-RDIMM-timing-and-.patch
new file mode 100644
index 00000000..405cee33
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0074-amd-amdmct-mct_ddr3-Add-Family-15h-RDIMM-timing-and-.patch
@@ -0,0 +1,513 @@
+From 3d935128d5120ab48978aabfa2e99fe7d11992e1 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 17:07:57 -0500
+Subject: [PATCH 074/146] amd/amdmct/mct_ddr3: Add Family 15h RDIMM timing and
+ ODT values
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 432 ++++++++++++++++++---------
+ 1 file changed, 295 insertions(+), 137 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 4a47ee8..c35e972 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -822,28 +822,12 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+- /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+- if (MaxDimmsInstallable == 1) {
+- if (MemClkFreq == 0x4) {
+- /* DDR3-667 */
+- calibration_code = 0x00112222;
+- }
+- else if (MemClkFreq == 0x6) {
+- /* DDR3-800 */
+- calibration_code = 0x10112222;
+- } else if (MemClkFreq == 0xa) {
+- /* DDR3-1066 */
+- calibration_code = 0x20112222;
+- } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+- /* DDR3-1333 - DDR3-1600 */
+- calibration_code = 0x30112222;
+- } else if (MemClkFreq == 0x16) {
+- /* DDR3-1866 */
+- calibration_code = 0x30332222;
+- }
+- } else if (MaxDimmsInstallable == 2) {
+- if (dimm_count == 1) {
+- /* 1 DIMM detected */
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 74 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x00112222;
+@@ -856,36 +840,137 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+ /* DDR3-1333 - DDR3-1600 */
+ calibration_code = 0x30112222;
++ } else if (MemClkFreq == 0x16) {
++ /* DDR3-1866 */
++ calibration_code = 0x30332222;
+ }
+- } else if (dimm_count == 2) {
+- /* 2 DIMMs detected */
++
++ if (rank_count_dimm0 == 4) {
++ calibration_code &= ~(0xff << 16);
++ calibration_code |= 0x22 << 16;
++ }
++ } else if (MaxDimmsInstallable == 2) {
+ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
++
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00112222;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x10112222;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x20112222;
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ calibration_code = 0x30112222;
++ }
++
++ if ((rank_count_dimm0 == 4) || (rank_count_dimm1 == 4)) {
++ calibration_code &= ~(0xff << 16);
++ calibration_code |= 0x22 << 16;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x10222222;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x20222222;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x30222222;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x30222222;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ calibration_code = 0x30222222;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++ /* TODO
++ * LRDIMM support unimplemented
++ */
++ } else {
++ /* UDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+- calibration_code = 0x10222222;
++ calibration_code = 0x00112222;
+ } else if (MemClkFreq == 0x6) {
+ /* DDR3-800 */
+- calibration_code = 0x20222222;
++ calibration_code = 0x10112222;
+ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+- calibration_code = 0x30222222;
+- } else if (MemClkFreq == 0xe) {
+- /* DDR3-1333 */
+- calibration_code = 0x30222222;
+- } else if (MemClkFreq == 0x12) {
+- /* DDR3-1600 */
+- if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
+- calibration_code = 0x30222222;
+- else
+- calibration_code = 0x30112222;
++ calibration_code = 0x20112222;
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ calibration_code = 0x30112222;
++ } else if (MemClkFreq == 0x16) {
++ /* DDR3-1866 */
++ calibration_code = 0x30332222;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00112222;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x10112222;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x20112222;
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ calibration_code = 0x30112222;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x10222222;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x20222222;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x30222222;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x30222222;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ calibration_code = 0x30222222;
++ else
++ calibration_code = 0x30112222;
++ }
+ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
+ }
+- } else if (MaxDimmsInstallable == 3) {
+- /* TODO
+- * 3 DIMM/channel support unimplemented
+- */
+ }
+ } else {
+ /* TODO
+@@ -917,43 +1002,71 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+- /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+- if (MaxDimmsInstallable == 1) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
+- if (MemClkFreq == 0x4) {
+- /* DDR3-667 */
+- if (rank_count_dimm0 == 1)
+- calibration_code = 0x00000000;
+- else
+- calibration_code = 0x003b0000;
+- } else if (MemClkFreq == 0x6) {
+- /* DDR3-800 */
+- if (rank_count_dimm0 == 1)
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 74 */
++ if (MaxDimmsInstallable == 1) {
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800*/
+ calibration_code = 0x00000000;
+- else
+- calibration_code = 0x003b0000;
+- } else if (MemClkFreq == 0xa) {
+- /* DDR3-1066 */
+- calibration_code = 0x00383837;
+- } else if (MemClkFreq == 0xe) {
+- /* DDR3-1333 */
+- calibration_code = 0x00363635;
+- } else if (MemClkFreq == 0x12) {
+- /* DDR3-1600 */
+- if (rank_count_dimm0 == 1)
+- calibration_code = 0x00353533;
+- else
+- calibration_code = 0x00003533;
+- } else if (MemClkFreq == 0x16) {
+- /* DDR3-1866 */
+- calibration_code = 0x00333330;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x003c3c3c;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x003a3a3a;
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ calibration_code = 0x00393939;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800*/
++ calibration_code = 0x00000000;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x00393c39;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00373a37;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ calibration_code = 0x00363936;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800*/
++ calibration_code = 0x00000000;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x003a3c3a;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00383a38;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ calibration_code = 0x00353935;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
+ }
+- } else if (MaxDimmsInstallable == 2) {
+- if (dimm_count == 1) {
+- /* 1 DIMM detected */
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++ /* TODO
++ * LRDIMM support unimplemented
++ */
++ } else {
++ /* UDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
+ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ if (rank_count_dimm0 == 1)
+@@ -978,34 +1091,68 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ calibration_code = 0x00353533;
+ else
+ calibration_code = 0x00003533;
++ } else if (MemClkFreq == 0x16) {
++ /* DDR3-1866 */
++ calibration_code = 0x00333330;
+ }
+- } else if (dimm_count == 2) {
+- /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
+- if (MemClkFreq == 0x4) {
+- /* DDR3-667 */
+- calibration_code = 0x00390039;
+- } else if (MemClkFreq == 0x6) {
+- /* DDR3-800 */
+- calibration_code = 0x00390039;
+- } else if (MemClkFreq == 0xa) {
+- /* DDR3-1066 */
+- calibration_code = 0x003a3a3a;
+- } else if (MemClkFreq == 0xe) {
+- /* DDR3-1333 */
+- calibration_code = 0x00003939;
+- } else if (MemClkFreq == 0x12) {
+- /* DDR3-1600 */
+- if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
+- calibration_code = 0x00003738;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x00383837;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00363635;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00353533;
++ else
++ calibration_code = 0x00003533;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-667 */
++ calibration_code = 0x00390039;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-800 */
++ calibration_code = 0x00390039;
++ } else if (MemClkFreq == 0xa) {
++ /* DDR3-1066 */
++ calibration_code = 0x003a3a3a;
++ } else if (MemClkFreq == 0xe) {
++ /* DDR3-1333 */
++ calibration_code = 0x00003939;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ calibration_code = 0x00003738;
++ }
+ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
+ }
+- } else if (MaxDimmsInstallable == 3) {
+- /* TODO
+- * 3 DIMM/channel support unimplemented
+- */
+ }
+ } else {
+ /* TODO
+@@ -1037,55 +1184,66 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+- /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+- if (MaxDimmsInstallable == 1) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
+- if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+- || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+- /* DDR3-667 - DDR3-1333 */
+- slow_access = 0;
+- } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+- /* DDR3-1600 - DDR3-1866 */
+- if (rank_count_dimm0 == 1)
+- slow_access = 0;
+- else
+- slow_access = 1;
+- }
+- } else if (MaxDimmsInstallable == 2) {
+- if (dimm_count == 1) {
+- /* 1 DIMM detected */
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 74 */
++ slow_access = 0;
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 75 */
++ slow_access = 0;
++ } else {
++ /* UDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
++ if (MaxDimmsInstallable == 1) {
+ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
++
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+ /* DDR3-667 - DDR3-1333 */
+ slow_access = 0;
+- } else if (MemClkFreq == 0x12) {
+- /* DDR3-1600 */
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
+ if (rank_count_dimm0 == 1)
+ slow_access = 0;
+ else
+ slow_access = 1;
+ }
+- } else if (dimm_count == 2) {
+- /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+-
+- if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+- || (MemClkFreq == 0xa)) {
+- /* DDR3-667 - DDR3-1066 */
+- slow_access = 0;
+- } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+- /* DDR3-1333 - DDR3-1600 */
+- slow_access = 1;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
++ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
++ /* DDR3-667 - DDR3-1333 */
++ slow_access = 0;
++ } else if (MemClkFreq == 0x12) {
++ /* DDR3-1600 */
++ if (rank_count_dimm0 == 1)
++ slow_access = 0;
++ else
++ slow_access = 1;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
++ || (MemClkFreq == 0xa)) {
++ /* DDR3-667 - DDR3-1066 */
++ slow_access = 0;
++ } else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
++ /* DDR3-1333 - DDR3-1600 */
++ slow_access = 1;
++ }
+ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
+ }
+- } else if (MaxDimmsInstallable == 3) {
+- /* TODO
+- * 3 DIMM/channel support unimplemented
+- */
+ }
+ } else {
+ /* TODO
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0075-northbridge-amd-amdmct-mct_ddr3-Attempt-to-recover-f.patch b/resources/libreboot/patch/kgpe-d16/0075-northbridge-amd-amdmct-mct_ddr3-Attempt-to-recover-f.patch
new file mode 100644
index 00000000..64369229
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0075-northbridge-amd-amdmct-mct_ddr3-Attempt-to-recover-f.patch
@@ -0,0 +1,254 @@
+From 46ebd28412a108fc6329118acb53345b44feea4c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 18:08:53 -0500
+Subject: [PATCH 075/146] northbridge/amd/amdmct/mct_ddr3: Attempt to recover
+ from phy training errors
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c | 68 +++++++++++++++++++------
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 36 ++++++++++---
+ 2 files changed, 83 insertions(+), 21 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+index 5e81808..539cb0d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+@@ -18,11 +18,11 @@
+ * Foundation, Inc.
+ */
+
+-static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
++static uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+-static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
++static uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+-static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
++static uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+ static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+ static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+@@ -100,11 +100,12 @@ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
+ }
+
+
+-static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
++static uint8_t PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u8 dimm;
+ u16 DIMMValid;
++ uint8_t status = 0;
+ void *DCTPtr;
+
+ dct &= 1;
+@@ -121,19 +122,22 @@ static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ PrepareC_DCT(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1))) {
+- AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+- AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+- AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ status |= AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ status |= AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
++ status |= AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+ }
+ }
+ }
++
++ return status;
+ }
+
+-static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
++static uint8_t PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ u8 dimm;
+ u16 DIMMValid;
++ uint8_t status = 0;
+ void *DCTPtr;
+
+ dct &= 1;
+@@ -163,12 +167,14 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1))) {
+- AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+- AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+- AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ status |= AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ status |= AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
++ status |= AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+ }
+ }
+ }
++
++ return status;
+ }
+
+ static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
+@@ -183,6 +189,8 @@ static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
+ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, uint8_t Pass)
+ {
++ uint8_t status;
++ uint8_t timeout;
+ uint16_t final_target_freq;
+
+ pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
+@@ -201,8 +209,21 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ }
+
+ if (Pass == FirstPass) {
+- PhyWLPass1(pMCTstat, pDCTstat, 0);
+- PhyWLPass1(pMCTstat, pDCTstat, 1);
++ timeout = 0;
++ do {
++ status = 0;
++ timeout++;
++ status |= PhyWLPass1(pMCTstat, pDCTstat, 0);
++ status |= PhyWLPass1(pMCTstat, pDCTstat, 1);
++ if (status)
++ printk(BIOS_INFO,
++ "%s: Retrying write levelling due to invalid value(s) detected in first phase\n",
++ __func__);
++ } while (status && (timeout < 8));
++ if (status)
++ printk(BIOS_INFO,
++ "%s: Uncorrectable invalid value(s) detected in first phase of write levelling\n",
++ __func__);
+ }
+
+ if (Pass == SecondPass) {
+@@ -211,6 +232,7 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ * NOTE: BIOS must program both DCTs to the same frequency.
+ * NOTE: Fam15h steps the frequency, Fam10h slams the frequency.
+ */
++ uint8_t global_phy_training_status = 0;
+ final_target_freq = pDCTstat->TargetFreq;
+
+ while (pDCTstat->Speed != final_target_freq) {
+@@ -219,12 +241,28 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ else
+ pDCTstat->TargetFreq = final_target_freq;
+ SetTargetFreq(pMCTstat, pDCTstat);
+- PhyWLPass2(pMCTstat, pDCTstat, 0);
+- PhyWLPass2(pMCTstat, pDCTstat, 1);
++ timeout = 0;
++ do {
++ status = 0;
++ timeout++;
++ status |= PhyWLPass2(pMCTstat, pDCTstat, 0);
++ status |= PhyWLPass2(pMCTstat, pDCTstat, 1);
++ if (status)
++ printk(BIOS_INFO,
++ "%s: Retrying write levelling due to invalid value(s) detected in last phase\n",
++ __func__);
++ } while (status && (timeout < 8));
++ global_phy_training_status |= status;
+ }
+
+ pDCTstat->TargetFreq = final_target_freq;
+
++ if (global_phy_training_status)
++ printk(BIOS_WARNING,
++ "%s: Uncorrectable invalid value(s) detected in second phase of write levelling; "
++ "continuing but system may be unstable!\n",
++ __func__);
++
+ uint8_t dct;
+ for (dct = 0; dct < 2; dct++) {
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 3995a70..72c74e6 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -54,7 +54,7 @@ static int32_t abs(int32_t val) {
+ */
+
+ /*-----------------------------------------------------------------------------
+- * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
++ * uint8_t AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
+ * u8 Dimm, u8 Pass)
+ *
+ * Description:
+@@ -71,7 +71,7 @@ static int32_t abs(int32_t val) {
+ * OUT
+ *-----------------------------------------------------------------------------
+ */
+-void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ u8 dct, u8 dimm, u8 pass)
+ {
+ u8 ByteLane;
+@@ -174,12 +174,15 @@ void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
+ }
+
+ pDCTData->WLCriticalGrossDelayPrevPass = 0x1f;
++
++ return 0;
+ }
+
+-void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ u8 dct, u8 dimm, u8 pass)
+ {
+ u8 ByteLane;
++ uint8_t status = 0;
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+
+ if (is_fam15h()) {
+@@ -206,19 +209,38 @@ void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
+
+ /* Compensate for occasional noise/instability causing sporadic training failure */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ uint8_t faulty_value_detected = 0;
+ uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f);
+ uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
+- if (abs(total_delay_phy - total_delay_seed) > 0x20) {
+- printk(BIOS_DEBUG, "%s: overriding faulty phy value (seed: %04x phy: %04x step: %04x)\n", __func__,
++ if (pass == FirstPass) {
++ /* Allow a somewhat higher step threshold on the first pass
++ * For the most part, as long as the phy isn't stepping
++ * several clocks at once the values are probably valid.
++ */
++ if (abs(total_delay_phy - total_delay_seed) > 0x30)
++ faulty_value_detected = 1;
++ } else {
++ /* Stepping memory clocks between adjacent allowed frequencies
++ * should not yield large phy value differences...
++ */
++
++ if (abs(total_delay_phy - total_delay_seed) > 0x20)
++ faulty_value_detected = 1;
++ }
++ if (faulty_value_detected) {
++ printk(BIOS_INFO, "%s: overriding faulty phy value (seed: %04x phy: %04x step: %04x)\n", __func__,
+ total_delay_seed, total_delay_phy, abs(total_delay_phy - total_delay_seed));
+ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
+ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
++ status = 1;
+ }
+ }
+ }
++
++ return status;
+ }
+
+-void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
++uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ u8 dct, u8 dimm, u8 pass)
+ {
+ u8 ByteLane;
+@@ -285,6 +307,8 @@ void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
+ * to the normal operating termination:
+ */
+ prepareDimms(pMCTstat, pDCTstat, dct, dimm, FALSE);
++
++ return 0;
+ }
+
+ /*----------------------------------------------------------------------------
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0076-northbridge-amd-amdmct-mct_ddr3-Work-around-strange-.patch b/resources/libreboot/patch/kgpe-d16/0076-northbridge-amd-amdmct-mct_ddr3-Work-around-strange-.patch
new file mode 100644
index 00000000..c269358f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0076-northbridge-amd-amdmct-mct_ddr3-Work-around-strange-.patch
@@ -0,0 +1,40 @@
+From d78d6f37c04273b66bc0de4ea62deea5c033bf97 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 25 Jun 2015 18:37:45 -0500
+Subject: [PATCH 076/146] northbridge/amd/amdmct/mct_ddr3: Work around strange
+ phy training issue
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 72c74e6..9c18d12 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -207,6 +207,22 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+
+ pDCTData->WLCriticalGrossDelayPrevPass = cgd;
+
++ if (pDCTstat->Speed != pDCTstat->TargetFreq) {
++ /* FIXME
++ * Using the Pass 1 training values causes major phy training problems on
++ * all Family 15h processors I tested (Pass 1 values are randomly too high,
++ * and Pass 2 cannot lock).
++ * Figure out why this is and fix it, then remove the bypass code below...
++ */
++ if (pass == FirstPass) {
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
++ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
++ }
++ return 0;
++ }
++ }
++
+ /* Compensate for occasional noise/instability causing sporadic training failure */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ uint8_t faulty_value_detected = 0;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0077-northbridge-amd-amdmct-mct_ddr3-Add-additional-debug.patch b/resources/libreboot/patch/kgpe-d16/0077-northbridge-amd-amdmct-mct_ddr3-Add-additional-debug.patch
new file mode 100644
index 00000000..37de6316
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0077-northbridge-amd-amdmct-mct_ddr3-Add-additional-debug.patch
@@ -0,0 +1,118 @@
+From 8f15507b4cf857a15d81bf5aaa8447b339a60645 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 26 Jun 2015 00:17:10 -0500
+Subject: [PATCH 077/146] northbridge/amd/amdmct/mct_ddr3: Add additional
+ debug trace statements
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 8 ++++++++
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 16 ++++++++++++++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index c35e972..e7eb6af 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -5804,7 +5804,11 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
+ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ mct_ProgramODT_D(pMCTstat, pDCTstat, dct);
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+@@ -5814,6 +5818,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* FIXME
+ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+ * For now assume a maximum of 2 DIMMs per channel can be installed
+@@ -6128,6 +6134,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x183, odt_pattern_2);
+ }
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 51cbf16..380c5f2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -192,9 +192,13 @@ static void mct_DCTAccessDone(struct DCTStatStruc *pDCTstat, u8 dct)
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ do {
+ val = Get_NB32_DCT(dev, dct, 0x98);
+ } while (!(val & (1 << DctAccessDone)));
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting, u8 MrsChipSel, u8 dct)
+@@ -239,6 +243,8 @@ static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS)
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ val = Get_NB32_DCT(dev, dct, 0x7c);
+ val &= ~0x00ffffff;
+ val |= EMRS;
+@@ -248,6 +254,8 @@ static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS)
+ do {
+ val = Get_NB32_DCT(dev, dct, 0x7c);
+ } while (val & (1 << SendMrsCmd));
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+@@ -557,6 +565,8 @@ static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct)
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /*1.Program MrsAddress[10]=1
+ 2.Set SendZQCmd=1
+ */
+@@ -573,6 +583,8 @@ static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct)
+
+ /* 4.Wait 512 MEMCLKs */
+ mct_Wait(300);
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+@@ -582,6 +594,8 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+ /* 3.Program F2x[1,0]7C[EnDramInit]=1 */
+ dword = Get_NB32_DCT(dev, dct, 0x7c);
+@@ -663,4 +677,6 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ Set_NB32_DCT(dev, dct, 0x7C, dword);
+ mct_DCTAccessDone(pDCTstat, dct);
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0078-northbridge-amd-amdmct-mct_ddr3-Fix-null-pointer-acc.patch b/resources/libreboot/patch/kgpe-d16/0078-northbridge-amd-amdmct-mct_ddr3-Fix-null-pointer-acc.patch
new file mode 100644
index 00000000..2b4ab06e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0078-northbridge-amd-amdmct-mct_ddr3-Fix-null-pointer-acc.patch
@@ -0,0 +1,238 @@
+From e708c2a00f881e5bb37a9b224f72109e2c9ffecc Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 26 Jun 2015 12:17:48 -0500
+Subject: [PATCH 078/146] northbridge/amd/amdmct/mct_ddr3: Fix null pointer
+ access and related hangs
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 58 ++++++++++++++------------
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 8 ++--
+ 2 files changed, 34 insertions(+), 32 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index e7eb6af..61293ba 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -826,7 +826,7 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ /* RDIMM */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 74 */
+ if (MaxDimmsInstallable == 1) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -850,8 +850,8 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ calibration_code |= 0x22 << 16;
+ }
+ } else if (MaxDimmsInstallable == 2) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+@@ -875,8 +875,8 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ }
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -943,8 +943,8 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ }
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -1065,7 +1065,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ /* UDIMM */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+ if (MaxDimmsInstallable == 1) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -1098,7 +1098,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ } else if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -1127,8 +1127,8 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ }
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+@@ -1196,7 +1196,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ /* UDIMM */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+ if (MaxDimmsInstallable == 1) {
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+@@ -1212,7 +1212,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ } else if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+@@ -1227,8 +1227,8 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ }
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa)) {
+@@ -5840,14 +5840,18 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ uint8_t write_odt_delay;
+ uint8_t read_odt_delay;
+
++ /* NOTE
++ * Rank count per DIMM and DCT is encoded by pDCTstat->DimmRanks[(<dimm number> * 2) + dct]
++ */
++
+ /* Select appropriate ODT pattern for installed DIMMs
+ * Refer to the Fam15h BKDG Rev. 3.14, page 149 onwards
+ */
+- if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED]) {
++ if (pDCTstat->Status & (1 << SB_Registered)) {
+ if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+ if (rank_count_dimm1 == 1) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x00000000;
+@@ -5872,8 +5876,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ }
+ } else {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x01010202;
+@@ -5911,7 +5915,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ odt_pattern_2 = 0x00000000;
+ odt_pattern_3 = 0x00000000;
+ }
+- } else if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+ /* TODO
+ * Load reduced dimms UNIMPLEMENTED
+ */
+@@ -5923,7 +5927,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+ if (rank_count_dimm1 == 1) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x00000000;
+@@ -5959,7 +5963,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+- if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+ /* TODO
+ * Load reduced dimms UNIMPLEMENTED
+ */
+@@ -6019,11 +6023,11 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ /* Select appropriate ODT pattern for installed DIMMs
+ * Refer to the Fam10h BKDG Rev. 3.62, page 120 onwards
+ */
+- if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) {
++ if (pDCTstat->Status & (1 << SB_Registered)) {
+ if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + i];
+ if (rank_count_dimm1 == 1) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x00000000;
+@@ -6048,8 +6052,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ }
+ } else {
+ /* 2 DIMMs detected */
+- rank_count_dimm0 = pDCTstat->C_DCTPtr[i]->DimmRanks[0];
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + i];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + i];
+ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x01010202;
+@@ -6091,7 +6095,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+- rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + i];
+ if (rank_count_dimm1 == 1) {
+ odt_pattern_0 = 0x00000000;
+ odt_pattern_1 = 0x00000000;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 380c5f2..c7d7463 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -38,10 +38,9 @@ static uint8_t fam15_dimm_dic(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_
+ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+ {
+ uint8_t term = 0;
+- sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+- uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
++ uint8_t number_of_dimms = pDCTstat->MAdimms[dct];
+ uint8_t frequency_index;
+- uint8_t rank_count = pDCTData->DimmRanks[dimm];
++ uint8_t rank_count = pDCTstat->DimmRanks[(dimm * 2) + dct];
+
+ if (is_fam15h())
+ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+@@ -101,8 +100,7 @@ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t d
+ static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+ {
+ uint8_t term = 0;
+- sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+- uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
++ uint8_t number_of_dimms = pDCTstat->MAdimms[dct];
+ uint8_t frequency_index;
+
+ if (is_fam15h())
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0079-northbridge-amd-amdmct-mct_ddr3-Add-missing-Family-1.patch b/resources/libreboot/patch/kgpe-d16/0079-northbridge-amd-amdmct-mct_ddr3-Add-missing-Family-1.patch
new file mode 100644
index 00000000..4993062d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0079-northbridge-amd-amdmct-mct_ddr3-Add-missing-Family-1.patch
@@ -0,0 +1,266 @@
+From 7a77252a55fa5de541a7e6c687fcd42168f86d55 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 26 Jun 2015 14:15:57 -0500
+Subject: [PATCH 079/146] northbridge/amd/amdmct/mct_ddr3: Add missing Family
+ 15h RDIMM Rtt values
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 4 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 201 +++++++++++++++++++++++++-
+ 2 files changed, 198 insertions(+), 7 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 61293ba..68ff3f8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -897,8 +897,8 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ }
+ } else if (MaxDimmsInstallable == 3) {
+ /* TODO
+- * 3 DIMM/channel support unimplemented
+- */
++ * 3 DIMM/channel support unimplemented
++ */
+ }
+ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+ /* LRDIMM */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index c7d7463..dfbd2d9 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -42,6 +42,10 @@ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t d
+ uint8_t frequency_index;
+ uint8_t rank_count = pDCTstat->DimmRanks[(dimm * 2) + dct];
+
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++ uint8_t rank_count_dimm2;
++
+ if (is_fam15h())
+ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+ else
+@@ -54,11 +58,80 @@ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t d
+ uint8_t MaxDimmsInstallable = 2;
+
+ if (is_fam15h()) {
+- if (pDCTstat->Status & (1 << SB_Registered)) {
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+ /* TODO
+- * RDIMM unimplemented
++ * LRDIMM unimplemented
+ */
++ } else if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 57 */
++ if (MaxDimmsInstallable == 1) {
++ if ((frequency_index == 0x4) || (frequency_index == 0x6)
++ || (frequency_index == 0xa) || (frequency_index == 0xe)) {
++ /* DDR3-667 - DDR3-1333 */
++ if (rank_count < 3)
++ term = 0x0;
++ else
++ term = 0x2;
++ } else {
++ /* DDR3-1600 */
++ term = 0x0;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((frequency_index == 0x4) || (frequency_index == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if ((number_of_dimms == 1) && ((rank_count_dimm0 < 4)
++ && (rank_count_dimm1 < 4)))
++ term = 0x0;
++ else
++ term = 0x2;
++ } else if (frequency_index == 0xa) {
++ /* DDR3-1066 */
++ if (number_of_dimms == 1) {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4))
++ term = 0x0;
++ else
++ term = 0x2;
++ } else {
++ term = 0x1;
++ }
++ } else if (frequency_index == 0xe) {
++ /* DDR3-1333 */
++ term = 0x2;
++ } else {
++ /* DDR3-1600 */
++ if (number_of_dimms == 1)
++ term = 0x0;
++ else
++ term = 0x1;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ rank_count_dimm2 = pDCTstat->DimmRanks[(2 * 2) + dct];
++
++ if ((frequency_index == 0xa) || (frequency_index == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (rank_count_dimm2 < 4)
++ term = 0x1;
++ else
++ term = 0x2;
++ } else if (frequency_index == 0x12) {
++ /* DDR3-1600 */
++ term = 0x1;
++ } else {
++ term = 0x2;
++ }
++ }
++ } else {
++ /* TODO
++ * Other sockets unimplemented
++ */
++ }
+ } else {
++ /* UDIMM */
+ if (package_type == PT_GR) {
+ /* Socket G34: Fam15h BKDG v3.14 Table 56 */
+ if (MaxDimmsInstallable == 1) {
+@@ -103,6 +176,9 @@ static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t
+ uint8_t number_of_dimms = pDCTstat->MAdimms[dct];
+ uint8_t frequency_index;
+
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
+ if (is_fam15h())
+ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+ else
+@@ -120,10 +196,125 @@ static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t
+ * LRDIMM unimplemented
+ */
+ } else if (pDCTstat->Status & (1 << SB_Registered)) {
+- /* TODO
+- * RDIMM unimplemented
+- */
++ /* RDIMM */
++ if (package_type == PT_GR) {
++ /* Socket G34: Fam15h BKDG v3.14 Table 57 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++
++ if ((frequency_index == 0x4) || (frequency_index == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (rank_count_dimm0 < 4) {
++ term = 0x2;
++ } else {
++ if (!rank)
++ term = 0x2;
++ else
++ term = 0x0;
++ }
++ } else if (frequency_index == 0xa) {
++ /* DDR3-1066 */
++ term = 0x1;
++ } else if (frequency_index == 0xe) {
++ /* DDR3-1333 */
++ if (rank_count_dimm0 < 4) {
++ term = 0x1;
++ } else {
++ if (!rank)
++ term = 0x3;
++ else
++ term = 0x0;
++ }
++ } else {
++ term = 0x3;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((frequency_index == 0x4) || (frequency_index == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ if (number_of_dimms == 1) {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4))
++ term = 0x2;
++ else if (rank)
++ term = 0x0;
++ else
++ term = 0x2;
++ } else {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
++ term = 0x3;
++ } else {
++ if (rank_count_dimm0 == 4) {
++ if (rank_count_dimm1 == 1)
++ term = 0x5;
++ else
++ term = 0x1;
++ } else if (rank_count_dimm1 == 4) {
++ if (rank_count_dimm0 == 1)
++ term = 0x5;
++ else
++ term = 0x1;
++ }
++ if (rank)
++ term = 0x0;
++ }
++ }
++ } else if (frequency_index == 0xa) {
++ /* DDR3-1066 */
++ if (number_of_dimms == 1) {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4))
++ term = 0x1;
++ else if (rank)
++ term = 0x0;
++ else
++ term = 0x1;
++ } else {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
++ term = 0x3;
++ } else {
++ if (rank_count_dimm0 == 4) {
++ if (rank_count_dimm1 == 1)
++ term = 0x5;
++ else
++ term = 0x1;
++ } else if (rank_count_dimm1 == 4) {
++ if (rank_count_dimm0 == 1)
++ term = 0x5;
++ else
++ term = 0x1;
++ }
++ if (rank)
++ term = 0x0;
++ }
++ }
++ } else if (frequency_index == 0xe) {
++ /* DDR3-1333 */
++ if (number_of_dimms == 1) {
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4))
++ term = 0x1;
++ else if (rank)
++ term = 0x0;
++ else
++ term = 0x3;
++ } else {
++ term = 0x5;
++ }
++ } else {
++ /* DDR3-1600 */
++ if (number_of_dimms == 1)
++ term = 0x3;
++ else
++ term = 0x4;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ }
+ } else {
++ /* UDIMM */
+ if (package_type == PT_GR) {
+ /* Socket G34: Fam15h BKDG v3.14 Table 56 */
+ if (MaxDimmsInstallable == 1) {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0080-northbridge-amd-amdmct-mct_ddr3-Set-SkewMemClk-when-.patch b/resources/libreboot/patch/kgpe-d16/0080-northbridge-amd-amdmct-mct_ddr3-Set-SkewMemClk-when-.patch
new file mode 100644
index 00000000..5f3df4ae
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0080-northbridge-amd-amdmct-mct_ddr3-Set-SkewMemClk-when-.patch
@@ -0,0 +1,112 @@
+From 171fd97470dab51d069dc434e3e99ef64a8c77bd Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 26 Jun 2015 17:49:25 -0500
+Subject: [PATCH 080/146] northbridge/amd/amdmct/mct_ddr3: Set SkewMemClk when
+ both DCTs are in use
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 41 +++++++++++++++++++++------
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 2 +-
+ 2 files changed, 34 insertions(+), 9 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 68ff3f8..98a6952 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -2636,7 +2636,7 @@ static void DCTPreInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDC
+
+ /* Reset DCT registers */
+ ClearDCT_D(pMCTstat, pDCTstat, dct);
+- pDCTstat->stopDCT = 1; /*preload flag with 'disable' */
++ pDCTstat->stopDCT[dct] = 1; /* preload flag with 'disable' */
+
+ if (!is_fam15h()) {
+ /* Enable DDR3 support */
+@@ -2647,7 +2647,7 @@ static void DCTPreInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDC
+
+ /* Read the SPD information into the data structures */
+ if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_DIMMPresence Done\n");
++ printk(BIOS_DEBUG, "\t\tDCTPreInit_D: mct_DIMMPresence Done\n");
+ }
+ }
+
+@@ -2673,17 +2673,40 @@ static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTst
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
+ if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n");
+- pDCTstat->stopDCT = 0;
+- if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
+- printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n");
+- StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */
+- }
++ pDCTstat->stopDCT[dct] = 0;
+ }
+ }
+ }
+ }
+
+- if (pDCTstat->stopDCT) {
++
++}
++
++static void DCTFinalInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
++{
++ uint32_t dword;
++
++ /* Finalize DRAM init on a single node */
++ if (is_fam15h()) {
++ /* Set memory clock skew if needed */
++ if (dct == 0) {
++ if (!pDCTstat->stopDCT[0] && !pDCTstat->stopDCT[1]) {
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0d0fe00a);
++ dword |= (0x1 << 4); /* SkewMemClk = 1 */
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0d0fe00a, dword);
++ }
++ }
++ }
++
++ if (!pDCTstat->stopDCT[dct]) {
++ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
++ printk(BIOS_DEBUG, "\t\tDCTFinalInit_D: StartupDCT_D Start\n");
++ StartupDCT_D(pMCTstat, pDCTstat, dct);
++ printk(BIOS_DEBUG, "\t\tDCTFinalInit_D: StartupDCT_D Done\n");
++ }
++ }
++
++ if (pDCTstat->stopDCT[dct]) {
+ dword = 1 << DisDramInterface;
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
+
+@@ -4337,6 +4360,7 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+
+ /* Config. DCT0 for Ganged or unganged mode */
+ DCTInit_D(pMCTstat, pDCTstat, 0);
++ DCTFinalInit_D(pMCTstat, pDCTstat, 0);
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+ /* Do nothing goto exitDCTInit; any fatal errors? */
+ } else {
+@@ -4346,6 +4370,7 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ err_code = pDCTstat->ErrCode; /* save DCT0 errors */
+ pDCTstat->ErrCode = 0;
+ DCTInit_D(pMCTstat, pDCTstat, 1);
++ DCTFinalInit_D(pMCTstat, pDCTstat, 1);
+ if (pDCTstat->ErrCode == 2) /* DCT1 is not Running */
+ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */
+ } else {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 8c9da47..7bc392b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -338,7 +338,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ u8 Node_ID; /* Node ID of current controller */
+ uint8_t Internal_Node_ID; /* Internal Node ID of the current controller */
+ uint8_t Dual_Node_Package; /* 1=Dual node package (G34) */
+- uint8_t stopDCT; /* Set if the DCT will be stopped */
++ uint8_t stopDCT[2]; /* Set if the DCT will be stopped */
+ u8 ErrCode; /* Current error condition of Node
+ 0= no error
+ 1= Variance Error, DCT is running but not in an optimal configuration.
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0081-northbridge-amd-amdmct-mct_ddr3-Properly-indicate-cl.patch b/resources/libreboot/patch/kgpe-d16/0081-northbridge-amd-amdmct-mct_ddr3-Properly-indicate-cl.patch
new file mode 100644
index 00000000..4f9da004
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0081-northbridge-amd-amdmct-mct_ddr3-Properly-indicate-cl.patch
@@ -0,0 +1,56 @@
+From 9ee624980e31d6f489505388f417e06d121d0947 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 27 Jun 2015 17:52:18 -0500
+Subject: [PATCH 081/146] northbridge/amd/amdmct/mct_ddr3: Properly indicate
+ clobbered registers
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+index f6aa755..cc8d971 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+@@ -123,6 +123,9 @@ static void proc_CLFLUSH(u32 addr_hi)
+
+ static void WriteLNTestPattern(u32 addr_lo, u8 *buf_a, u32 line_num)
+ {
++ uint32_t step = 16;
++ uint32_t count = line_num * 4;
++
+ __asm__ volatile (
+ /*prevent speculative execution of following instructions*/
+ /* FIXME: needed ? */
+@@ -135,7 +138,7 @@ static void WriteLNTestPattern(u32 addr_lo, u8 *buf_a, u32 line_num)
+ "loop 1b\n\t"
+ "mfence\n\t"
+
+- :: "a" (addr_lo), "d" (16), "c" (line_num * 4), "b"(buf_a)
++ : "+a" (addr_lo), "+d" (step), "+c" (count), "+b" (buf_a) : :
+ );
+
+ }
+@@ -255,6 +258,10 @@ static void ReadMaxRdLat1CLTestPattern_D(u32 addr)
+
+ static void WriteMaxRdLat1CLTestPattern_D(u32 buf, u32 addr)
+ {
++ uint32_t addr_phys = addr << 8;
++ uint32_t step = 16;
++ uint32_t count = 3 * 4;
++
+ SetUpperFSbase(addr);
+
+ __asm__ volatile (
+@@ -267,7 +274,7 @@ static void WriteMaxRdLat1CLTestPattern_D(u32 buf, u32 addr)
+ "loop 1b\n\t"
+ "mfence\n\t"
+
+- :: "a" (addr<<8), "d" (16), "c" (3 * 4), "b"(buf)
++ : "+a" (addr_phys), "+d" (step), "+c" (count), "+b" (buf) : :
+ );
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0082-northbridge-amd-amdmct-mct_ddr3-Fix-Family-10h-boot-.patch b/resources/libreboot/patch/kgpe-d16/0082-northbridge-amd-amdmct-mct_ddr3-Fix-Family-10h-boot-.patch
new file mode 100644
index 00000000..d7d15ccd
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0082-northbridge-amd-amdmct-mct_ddr3-Fix-Family-10h-boot-.patch
@@ -0,0 +1,98 @@
+From 3d30e829f9630d588dee70215b67343c75996520 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 27 Jun 2015 17:52:45 -0500
+Subject: [PATCH 082/146] northbridge/amd/amdmct/mct_ddr3: Fix Family 10h boot
+ failure
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 28 +++++++++++++++++++-------
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 8 +++++++-
+ 2 files changed, 28 insertions(+), 8 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 98a6952..6de8140 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1622,6 +1622,11 @@ restartinit:
+ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
+ mctHookAfterHTMap();
+
++ if (!is_fam15h()) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++ }
++
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctHookAfterCPU\n");
+ mctHookAfterCPU(); /* Setup external northbridge(s) */
+
+@@ -1645,6 +1650,11 @@ restartinit:
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+ DQSTiming_D(pMCTstat, pDCTstatA, allow_config_restore); /* Get Receiver Enable and DQS signal timing*/
+
++ if (!is_fam15h()) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++ }
++
+ if (!allow_config_restore) {
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
+ mct_OtherTiming(pMCTstat, pDCTstatA);
+@@ -1665,11 +1675,13 @@ restartinit:
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ }
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
+- CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
++ if (is_fam15h()) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+- UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
++ }
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -6332,11 +6344,13 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ DramMRS |= 1 << 1;
+
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
+- dword |= DramMRS;
+- if (is_fam15h())
++ if (is_fam15h()) {
++ dword |= DramMRS;
+ dword &= ~0x00800003;
+- else
++ } else {
+ dword &= ~0x00fc2f8f;
++ dword |= DramMRS;
++ }
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 039fcf8..1d41aa4 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -909,9 +909,15 @@ static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
+ * Flush the receiver FIFO
+ * Write one full cache line of non-0x55/0xaa data to one of the test addresses, then read it back to flush the FIFO
+ */
+-
++ /* FIXME
++ * This does not seem to be needed, and has a tendency to lock up the
++ * boot process while attempting to write the test pattern.
++ */
++#if 0
++ SetUpperFSbase(TestAddr0);
+ WriteLNTestPattern(TestAddr0 << 8, (uint8_t *)TestPattern2_D, 1);
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0);
++#endif
+ }
+ MaxDelay_CH[Channel] = CTLRMaxDelay;
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0083-src-southbridge-amd-sr5650-Always-configure-lane-dir.patch b/resources/libreboot/patch/kgpe-d16/0083-src-southbridge-amd-sr5650-Always-configure-lane-dir.patch
new file mode 100644
index 00000000..81602d48
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0083-src-southbridge-amd-sr5650-Always-configure-lane-dir.patch
@@ -0,0 +1,48 @@
+From 4ac11f31fdfe91eb2d6e998f293f88fb0a32452c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 3 Jul 2015 17:16:22 -0500
+Subject: [PATCH 083/146] src/southbridge/amd/sr5650: Always configure lane
+ director on startup
+
+On the ASUS KGPE-D16 it was noted that the pin straps did not properly
+configure the lane director hardware, causing link training failure
+on NIC B. Forcing coreboot to always reconfigure the lane director
+on startup resolves this problem.
+---
+ src/southbridge/amd/sr5650/pcie.c | 13 +++----------
+ 1 file changed, 3 insertions(+), 10 deletions(-)
+
+diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
+index 79f2a5f..09ce217 100644
+--- a/src/southbridge/amd/sr5650/pcie.c
++++ b/src/southbridge/amd/sr5650/pcie.c
+@@ -862,8 +862,6 @@ void sr56x0_lock_hwinitreg(void)
+ void config_gpp_core(device_t nb_dev, device_t sb_dev)
+ {
+ u32 reg;
+- struct southbridge_amd_sr5650_config *cfg =
+- (struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
+
+ reg = nbmisc_read_index(nb_dev, 0x20);
+ if (AtiPcieCfg.Config & PCIE_ENABLE_STATIC_DEV_REMAP)
+@@ -879,14 +877,9 @@ void config_gpp_core(device_t nb_dev, device_t sb_dev)
+ reg &= ~((1 << 31) | (1 << 15) | (1 << 13)); //De-asserts
+ nbmisc_write_index(nb_dev, 0x8, reg);
+
+- reg = nbmisc_read_index(nb_dev, 0x67); /* get STRAP_BIF_LINK_CONFIG at bit 0-4 */
+- if (cfg->gpp3a_configuration != (reg & 0x1F))
+- switching_gpp3a_configurations(nb_dev, sb_dev);
+- reg = nbmisc_read_index(nb_dev, 0x8); /* get MULTIPORT_CONFIG_GPP1 MULTIPORT_CONFIG_CONFIG_GPP2 at bit 8,9 */
+- if ((cfg->gpp1_configuration << 8) != (reg & (1 << 8)))
+- switching_gpp1_configurations(nb_dev, sb_dev);
+- if ((cfg->gpp2_configuration << 9) != (reg & (1 << 9)))
+- switching_gpp2_configurations(nb_dev, sb_dev);
++ switching_gpp3a_configurations(nb_dev, sb_dev);
++ switching_gpp1_configurations(nb_dev, sb_dev);
++ switching_gpp2_configurations(nb_dev, sb_dev);
+ ValidatePortEn(nb_dev);
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0084-cpu-amd-model_10xxx-Fix-BSP-stack-corruption-on-32-c.patch b/resources/libreboot/patch/kgpe-d16/0084-cpu-amd-model_10xxx-Fix-BSP-stack-corruption-on-32-c.patch
new file mode 100644
index 00000000..c5275038
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0084-cpu-amd-model_10xxx-Fix-BSP-stack-corruption-on-32-c.patch
@@ -0,0 +1,26 @@
+From 50df46615e5505f7b649c20a08e0cfbcc7c34eb6 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 24 Jul 2015 17:34:29 -0500
+Subject: [PATCH 084/146] cpu/amd/model_10xxx: Fix BSP stack corruption on
+ 32-core Fam10 systems
+
+---
+ src/cpu/amd/model_10xxx/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/model_10xxx/Kconfig b/src/cpu/amd/model_10xxx/Kconfig
+index 79db42a..d6bcaa2 100644
+--- a/src/cpu/amd/model_10xxx/Kconfig
++++ b/src/cpu/amd/model_10xxx/Kconfig
+@@ -36,7 +36,7 @@ config DCACHE_BSP_STACK_SIZE
+
+ config DCACHE_BSP_STACK_SLUSH
+ hex
+- default 0x1000
++ default 0x4000
+
+ config DCACHE_AP_STACK_SIZE
+ hex
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0085-northbridge-amd-amdmct-mct_ddr3-Fix-RDIMM-errors-due.patch b/resources/libreboot/patch/kgpe-d16/0085-northbridge-amd-amdmct-mct_ddr3-Fix-RDIMM-errors-due.patch
new file mode 100644
index 00000000..351906e0
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0085-northbridge-amd-amdmct-mct_ddr3-Fix-RDIMM-errors-due.patch
@@ -0,0 +1,411 @@
+From 4faf00104dc3474340520213fc0e5f15f14e7146 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 25 Jul 2015 01:23:17 -0500
+Subject: [PATCH 085/146] northbridge/amd/amdmct/mct_ddr3: Fix RDIMM errors
+ due to undefined number of slots
+
+---
+ src/northbridge/amd/amdmct/mct/mct_d.h | 2 ++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 40 +++++++-----------------
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 2 ++
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 6 +---
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 12 ++-----
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 6 +---
+ src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 2 --
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 24 +++++---------
+ src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c | 11 ++++---
+ src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h | 4 ---
+ src/northbridge/amd/amdmct/wrappers/mcti_d.c | 7 +++++
+ 11 files changed, 41 insertions(+), 75 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct/mct_d.h b/src/northbridge/amd/amdmct/mct/mct_d.h
+index 6b6194d..34f7f4c 100644
+--- a/src/northbridge/amd/amdmct/mct/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct/mct_d.h
+@@ -688,6 +688,8 @@ struct DCTStatStruc { /* A per Node structure*/
+ xx0b = disable
+ yy1b = enable with DctSelIntLvAddr set to yyb */
+
++#define NV_MAX_DIMMS_PER_CH 64 /* Maximum number of DIMMs per channel */
++
+ /*===============================================================================
+ CBMEM storage
+ ===============================================================================*/
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 6de8140..030372d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -803,11 +803,7 @@ static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *p
+
+ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ uint8_t package_type;
+ uint32_t calibration_code = 0;
+@@ -877,7 +873,7 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
+ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x10222222;
+@@ -945,7 +941,7 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
+ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x10222222;
+@@ -983,11 +979,7 @@ static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCT
+
+ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ uint8_t package_type;
+ uint32_t calibration_code = 0;
+@@ -1066,7 +1058,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+ if (MaxDimmsInstallable == 1) {
+ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ if (rank_count_dimm0 == 1)
+@@ -1099,7 +1091,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ if (rank_count_dimm0 == 1)
+@@ -1129,7 +1121,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
+ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if (MemClkFreq == 0x4) {
+ /* DDR3-667 */
+ calibration_code = 0x00390039;
+@@ -1165,11 +1157,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+
+ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ uint8_t package_type;
+ uint32_t slow_access = 0;
+@@ -1197,7 +1185,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+ if (MaxDimmsInstallable == 1) {
+ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+ /* DDR3-667 - DDR3-1333 */
+@@ -1213,7 +1201,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+ /* DDR3-667 - DDR3-1333 */
+@@ -1229,7 +1217,7 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ /* 2 DIMMs detected */
+ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
+ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
+-
++
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+ || (MemClkFreq == 0xa)) {
+ /* DDR3-667 - DDR3-1066 */
+@@ -5857,11 +5845,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (is_fam15h()) {
+ /* Obtain number of DIMMs on channel */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 7bc392b..6b5d8c1 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -957,6 +957,8 @@ struct amd_s3_persistent_data {
+ xx0b = disable
+ yy1b = enable with DctSelIntLvAddr set to yyb */
+
++#define NV_MAX_DIMMS_PER_CH 64 /* Maximum number of DIMMs per channel */
++
+ /*===============================================================================
+ CBMEM storage
+ ===============================================================================*/
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index 32b447f..738304e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -23,11 +23,7 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+ {
+ u32 val;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
+ uint8_t cs_mux_45;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index dfbd2d9..6a2c2a7 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -51,11 +51,7 @@ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t d
+ else
+ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (is_fam15h()) {
+ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+@@ -184,11 +180,7 @@ static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t
+ else
+ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (is_fam15h()) {
+ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 1d41aa4..3655e84 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -105,11 +105,7 @@ static uint16_t fam15_receiver_enable_training_seed(struct DCTStatStruc *pDCTsta
+ uint32_t dword;
+ uint16_t seed = 0;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ uint8_t channel = dct;
+ if (package_type == PT_GR) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+index 0ff4484..6b63ba0 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+@@ -31,8 +31,6 @@ void PrepareC_MCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+ pDCTstat->C_MCTPtr->AgesaDelay = AgesaDelay;
+- pDCTstat->C_MCTPtr->PlatMaxTotalDimms = mctGet_NVbits(NV_MAX_DIMMS);
+- pDCTstat->C_MCTPtr->PlatMaxDimmsDct = pDCTstat->C_MCTPtr->PlatMaxTotalDimms >> 1;
+ }
+
+ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 9c18d12..26e1374 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -191,7 +191,7 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+
+ /* Calculate the Critical Gross Delay */
+- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ /* Calculate the gross delay differential for this lane */
+ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
+ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
+@@ -419,11 +419,7 @@ static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms
+ {
+ uint16_t term;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (number_of_dimms == 1) {
+ if (MaxDimmsInstallable < 3) {
+@@ -452,11 +448,7 @@ static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms
+ {
+ uint16_t term;
+
+- /* FIXME
+- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+- * For now assume a maximum of 2 DIMMs per channel can be installed
+- */
+- uint8_t MaxDimmsInstallable = 2;
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ if (number_of_dimms == 1) {
+ if (MaxDimmsInstallable < 3) {
+@@ -574,8 +566,8 @@ void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ if (number_of_dimms > 1) {
+ if (rank == 0) {
+ /* Get Rtt_WR for the current DIMM and rank */
+- uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]);
+-
++ uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]);
++
+ /* Convert dynamic termination code to corresponding nominal termination code */
+ if (dynamic_term == 0x200)
+ tempW1 = 0x04;
+@@ -584,13 +576,13 @@ void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ else
+ tempW1 = 0x0;
+ } else {
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+ }
+ } else {
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+ }
+ } else {
+- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c b/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c
+index c92143c..bb4c3c0 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -57,7 +58,7 @@ static u32 RttNomTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 d
+ u32 tempW1;
+ tempW1 = 0;
+ if (wl) {
+- switch (pMCTData->PlatMaxDimmsDct) {
++ switch (mctGet_NVbits(NV_MAX_DIMMS_PER_CH)) {
+ case 2:
+ /* 2 dimms per channel */
+ if (pDCTData->MaxDimmsInstalled == 1) {
+@@ -111,7 +112,7 @@ static u32 RttNomTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 d
+ ASSERT (FALSE);
+ }
+ } else {
+- switch (pMCTData->PlatMaxDimmsDct) {
++ switch (mctGet_NVbits(NV_MAX_DIMMS_PER_CH)) {
+ case 2:
+ /* 2 dimms per channel */
+ if ((pDCTData->DimmRanks[dimm] == 4) && (rank == 1)) {
+@@ -167,7 +168,7 @@ static u32 RttNomTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 d
+ */
+ static u32 RttNomNonTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl, u8 MemClkFreq, u8 rank)
+ {
+- if ((wl) && (pMCTData->PlatMaxDimmsDct == 2) && (pDCTData->DimmRanks[dimm] == 2) && (rank == 1)) {
++ if ((wl) && (mctGet_NVbits(NV_MAX_DIMMS_PER_CH) == 2) && (pDCTData->DimmRanks[dimm] == 2) && (rank == 1)) {
+ return 0x00; /* for non-target dimm during WL, the second rank of a DR dimm need to have Rtt_Nom = OFF */
+ } else {
+ return RttNomTargetRegDimm (pMCTData, pDCTData, dimm, FALSE, MemClkFreq, rank); /* otherwise, the same as target dimm in normal mode. */
+@@ -197,7 +198,7 @@ static u32 RttWrRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BO
+ if (wl) {
+ tempW1 = 0x00; /* Rtt_WR = OFF */
+ } else {
+- switch (pMCTData->PlatMaxDimmsDct) {
++ switch (mctGet_NVbits(NV_MAX_DIMMS_PER_CH)) {
+ case 2:
+ if (pDCTData->MaxDimmsInstalled == 1) {
+ if (pDCTData->DimmRanks[dimm] != 4) {
+@@ -262,7 +263,7 @@ static u8 WrLvOdtRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+ }
+ i += 2;
+ }
+- if (pMCTData->PlatMaxDimmsDct == 2) {
++ if (mctGet_NVbits(NV_MAX_DIMMS_PER_CH) == 2) {
+ if ((pDCTData->DimmRanks[dimm] == 4) && (pDCTData->MaxDimmsInstalled != 1)) {
+ if (dimm >= 2) {
+ WrLvOdt1 = (u8)bitTestReset (WrLvOdt1, (dimm - 2));
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+index 162340e..12e7c4a 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+@@ -109,10 +109,6 @@
+
+ typedef struct _sMCTStruct
+ {
+- u8 PlatMaxTotalDimms; /* IBV defined total number of DIMMs */
+- /* on a particular node */
+- u8 PlatMaxDimmsDct; /* IBV defined maximum number of */
+- /* DIMMs on a DCT */
+ void (*AgesaDelay)(u32 delayval); /* IBV defined Delay Function */
+ } sMCTStruct;
+
+diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+index 2376b20..85f117b 100644
+--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+@@ -74,6 +74,13 @@ static u16 mctGet_NVbits(u8 index)
+ val = MAX_DIMMS_SUPPORTED;
+ //val = 8;
+ break;
++ case NV_MAX_DIMMS_PER_CH:
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ val = 2;
++ break;
+ case NV_MAX_MEMCLK:
+ /* Maximum platform supported memclk */
+ val = MEM_MAX_LOAD_FREQ;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0086-amd-amdmct-mct_ddr3-Partially-fix-up-registered-DIMM.patch b/resources/libreboot/patch/kgpe-d16/0086-amd-amdmct-mct_ddr3-Partially-fix-up-registered-DIMM.patch
new file mode 100644
index 00000000..df19909d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0086-amd-amdmct-mct_ddr3-Partially-fix-up-registered-DIMM.patch
@@ -0,0 +1,957 @@
+From 00c038da36969597eb1c8e6e526ec0b0a9f12b43 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 26 Jul 2015 00:55:43 -0500
+Subject: [PATCH 086/146] amd/amdmct/mct_ddr3: Partially fix up registered
+ DIMMs on Fam10h
+
+Sufficient support has been added to allow booting with registered
+DIMMs on the KGPE-D16 in certain slots. ECC support needs additional
+work; the ECC data lanes appear to cause boot failures in some slots.
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 196 ++++++++++++++++++++++--
+ src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c | 32 ++--
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 58 ++++---
+ src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 151 ++++++++++++------
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 8 +
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 26 ++--
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 72 ++++++---
+ 7 files changed, 399 insertions(+), 144 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 030372d..28796bb 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -305,6 +305,120 @@ static uint16_t mhz_to_memclk_config(uint16_t freq)
+ return fam10h_mhz_to_memclk_config(freq) + 1;
+ }
+
++static uint32_t fam10h_address_timing_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
++
++ uint8_t package_type;
++ uint32_t calibration_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t MemClkFreq = (Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7) + 1;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam10h BKDG Rev. 3.62 section 2.8.9.5.8 Tables 60 - 61 */
++ if (MaxDimmsInstallable == 1) {
++ if (MemClkFreq == 0x4) {
++ /* DDR3-800 */
++ calibration_code = 0x00000000;
++ } else if (MemClkFreq == 0x5) {
++ /* DDR3-1066 */
++ calibration_code = 0x003c3c3c;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-1333 */
++ calibration_code = 0x003a3a3a;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if (MemClkFreq == 0x4) {
++ /* DDR3-800 */
++ calibration_code = 0x00000000;
++ } else if (MemClkFreq == 0x5) {
++ /* DDR3-1066 */
++ calibration_code = 0x003c3c3c;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-1333 */
++ calibration_code = 0x003a3a3a;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ if (MemClkFreq == 0x4) {
++ /* DDR3-800 */
++ calibration_code = 0x00000000;
++ } else if (MemClkFreq == 0x5) {
++ /* DDR3-1066 */
++ calibration_code = 0x003a3c3a;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-1333 */
++ calibration_code = 0x00383a38;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else {
++ /* UDIMM */
++ /* Fam10h BKDG Rev. 3.62 section 2.8.9.5.8 Table 56 */
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-800 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x003b0000;
++ } else if (MemClkFreq == 0x5) {
++ /* DDR3-1066 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x00380000;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-1333 */
++ if (rank_count_dimm0 == 1)
++ calibration_code = 0x00000000;
++ else
++ calibration_code = 0x00360000;
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (MemClkFreq == 0x4) {
++ /* DDR3-800 */
++ calibration_code = 0x00390039;
++ } else if (MemClkFreq == 0x5) {
++ /* DDR3-1066 */
++ calibration_code = 0x00350037;
++ } else if (MemClkFreq == 0x6) {
++ /* DDR3-1333 */
++ calibration_code = 0x00000035;
++ }
++ }
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return calibration_code;
++}
++
+ static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
+ {
+ uint8_t lrdimm = 0;
+@@ -999,7 +1113,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 74 */
+ if (MaxDimmsInstallable == 1) {
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+- /* DDR3-667 - DDR3-800*/
++ /* DDR3-667 - DDR3-800 */
+ calibration_code = 0x00000000;
+ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+@@ -1015,7 +1129,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+- /* DDR3-667 - DDR3-800*/
++ /* DDR3-667 - DDR3-800 */
+ calibration_code = 0x00000000;
+ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+@@ -1030,7 +1144,7 @@ static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDC
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+- /* DDR3-667 - DDR3-800*/
++ /* DDR3-667 - DDR3-800 */
+ calibration_code = 0x00000000;
+ } else if (MemClkFreq == 0xa) {
+ /* DDR3-1066 */
+@@ -1308,6 +1422,26 @@ static void read_spd_bytes(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++#ifdef DEBUG_DIMM_SPD
++static void dump_spd_bytes(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dimm)
++{
++ uint16_t byte;
++
++ printk(BIOS_DEBUG, "SPD dump for DIMM %d\n ", dimm);
++ for (byte = 0; byte < 16; byte++) {
++ printk(BIOS_DEBUG, "%02x ", byte);
++ }
++ for (byte = 0; byte < 256; byte++) {
++ if ((byte & 0xf) == 0x0) {
++ printk(BIOS_DEBUG, "\n%02x ", byte >> 4);
++ }
++ printk(BIOS_DEBUG, "%02x ", pDCTstat->spd_data.spd_bytes[dimm][byte]);
++ }
++ printk(BIOS_DEBUG, "\n");
++}
++#endif
++
+ static void calculate_and_store_spd_hashes(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+@@ -1504,12 +1638,14 @@ restartinit:
+ restore_mct_information_from_nvram(0);
+ pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
++ if (is_fam15h()) {
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
+
+- mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ }
+ }
+ } else {
+ NodesWmem = 0;
+@@ -1669,14 +1805,14 @@ restartinit:
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
+- }
+
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- struct DCTStatStruc *pDCTstat;
+- pDCTstat = pDCTstatA + Node;
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
+
+- mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
++ }
+ }
+
+ if (is_fam15h()) {
+@@ -2710,6 +2846,10 @@ static void DCTFinalInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *p
+ dword = 1 << DisDramInterface;
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
+
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x90);
++ dword &= ~(1 << ParEn);
++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x90, dword);
++
+ /* To maximize power savings when DisDramInterface=1b,
+ * all of the MemClkDis bits should also be set.
+ */
+@@ -3593,7 +3733,9 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ dword++;
+ }
+
+- if (!(Status & (1 << SB_Registered)))
++ if (Status & (1 << SB_Registered))
++ DramConfigLo |= 1 << ParEn; /* Registered DIMMs */
++ else
+ DramConfigLo |= 1 << UnBuffDimm; /* Unbuffered DIMMs */
+
+ if (mctGet_NVbits(NV_ECC_CAP))
+@@ -4082,6 +4224,9 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ if (status >= 0) { /* SPD access is ok */
+ pDCTstat->DIMMPresent |= 1 << i;
+ read_spd_bytes(pMCTstat, pDCTstat, i);
++#ifdef DEBUG_DIMM_SPD
++ dump_spd_bytes(pMCTstat, pDCTstat, i);
++#endif
+ crc_status = crcCheck(pDCTstat, i);
+ if (!crc_status) {
+ /* Try again in case there was a transient glitch */
+@@ -4377,6 +4522,10 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ val = 1 << DisDramInterface;
+ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
+
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
++ val &= ~(1 << ParEn);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
++
+ /* To maximize power savings when DisDramInterface=1b,
+ * all of the MemClkDis bits should also be set.
+ */
+@@ -4529,8 +4678,9 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ }
+ for (i=i_start; i<i_end; i++) {
+ index_reg = 0x98;
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A/B Output Driver Compensation Control */
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A/B Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A/B Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A/B Output Driver Compensation Control */
++ printk(BIOS_SPEW, "Programmed DCT %d timing/termination pattern %08x %08x\n", dct, pDCTstat->CH_ADDR_TMG[i], pDCTstat->CH_ODC_CTL[i]);
+ }
+
+ return pDCTstat->ErrCode;
+@@ -4587,11 +4737,19 @@ static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,
+ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+ val |= 1 << DisDramInterface;
+ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
++
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
++ val &= ~(1 << ParEn);
++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val);
+ }
+ if (pDCTstat->DIMMValidDCT[1] == 0) {
+ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+ val |= 1 << DisDramInterface;
+ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
++
++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
++ val &= ~(1 << ParEn);
++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
+ }
+
+ printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status);
+@@ -6022,6 +6180,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ dword &= ~(0xf); /* RdOdtTrnOnDly = read_odt_delay */
+ dword |= (read_odt_delay & 0xf);
+ Set_NB32_DCT(dev, dct, 0x240, dword);
++
++ printk(BIOS_SPEW, "Programmed ODT pattern %08x %08x %08x %08x\n", odt_pattern_0, odt_pattern_1, odt_pattern_2, odt_pattern_3);
+ } else if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+ if (pDCTstat->Speed == 3)
+ dword = 0x00000800;
+@@ -6157,6 +6317,8 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x181, odt_pattern_0);
+ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x182, odt_pattern_3);
+ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x183, odt_pattern_2);
++
++ printk(BIOS_SPEW, "Programmed ODT pattern %08x %08x %08x %08x\n", odt_pattern_0, odt_pattern_1, odt_pattern_2, odt_pattern_3);
+ }
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
+index 3df262b..4ae1aec 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
+@@ -21,7 +21,7 @@
+ /* AM3/ASB2/C32/G34 DDR3 */
+
+ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+- u32 *AddrTmgCTL, u32 *ODC_CTL,
++ u32 *ODC_CTL,
+ u8 *CMDmode);
+
+ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat,
+@@ -34,9 +34,14 @@ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed,
+ pDCTstat->MAload[dct],
+- &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
++ &(pDCTstat->CH_ODC_CTL[dct]),
+ &pDCTstat->_2Tmode);
+
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ pDCTstat->_2Tmode = 1; /* Disable slow access mode */
++ }
++ pDCTstat->CH_ADDR_TMG[dct] = fam10h_address_timing_compensation_code(pDCTstat, dct);
++
+ pDCTstat->CH_ODC_CTL[dct] |= 0x20000000; /* 60ohms */
+ }
+
+@@ -54,42 +59,25 @@ void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat,
+ * : ODC_CTL - Output Driver Compensation Control Register Value
+ * : CMDmode - CMD mode
+ */
+-static void Get_ChannelPS_Cfg0_D( u8 MAAdimms, u8 Speed, u8 MAAload,
+- u32 *AddrTmgCTL, u32 *ODC_CTL,
++static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
++ u32 *ODC_CTL,
+ u8 *CMDmode)
+ {
+- *AddrTmgCTL = 0;
+ *ODC_CTL = 0;
+ *CMDmode = 1;
+
+- if(MAAdimms == 1) {
+- if(MAAload >= 16) {
+- if(Speed == 4)
+- *AddrTmgCTL = 0x003B0000;
+- else if (Speed == 5)
+- *AddrTmgCTL = 0x00380000;
+- else if (Speed == 6)
+- *AddrTmgCTL = 0x00360000;
+- else
+- *AddrTmgCTL = 0x00340000;
+- } else {
+- *AddrTmgCTL = 0x00000000;
+- }
++ if (MAAdimms == 1) {
+ *ODC_CTL = 0x00113222;
+ *CMDmode = 1;
+ } else /* if(MAAdimms == 0) */ {
+ if(Speed == 4) {
+ *CMDmode = 1;
+- *AddrTmgCTL = 0x00390039;
+ } else if(Speed == 5) {
+ *CMDmode = 1;
+- *AddrTmgCTL = 0x00350037;
+ } else if(Speed == 6) {
+ *CMDmode = 2;
+- *AddrTmgCTL = 0x00000035;
+ } else {
+ *CMDmode = 2;
+- *AddrTmgCTL = 0x00000033;
+ }
+ *ODC_CTL = 0x00223323;
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 207a135..41b7244 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -241,37 +241,53 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u16 like, u8 scale, u8 ChipSel)
+ {
+- u8 DQSDelay0, DQSDelay1;
+- u16 DQSDelay;
++ uint8_t DQSDelay0, DQSDelay1;
++ int16_t delay_differential;
++ uint16_t DQSDelay;
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+- return;
+- }
++ pDCTstat->ByteLane = 0x2;
++ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
++ DQSDelay0 = pDCTstat->DQSDelay;
+
+- pDCTstat->ByteLane = like & 0xff;
+- GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+- DQSDelay0 = pDCTstat->DQSDelay;
++ pDCTstat->ByteLane = 0x3;
++ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
++ DQSDelay1 = pDCTstat->DQSDelay;
+
+- pDCTstat->ByteLane = (like >> 8) & 0xff;
+- GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+- DQSDelay1 = pDCTstat->DQSDelay;
++ if (pDCTstat->Direction == DQS_READDIR) {
++ DQSDelay = DQSDelay1;
++ } else {
++ delay_differential = (int16_t)DQSDelay1 - (int16_t)DQSDelay0;
++ delay_differential += (int16_t)DQSDelay1;
+
+- if (DQSDelay0>DQSDelay1) {
+- DQSDelay = DQSDelay0 - DQSDelay1;
++ DQSDelay = delay_differential;
++ }
+ } else {
+- DQSDelay = DQSDelay1 - DQSDelay0;
+- }
++ pDCTstat->ByteLane = like & 0xff;
++ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
++ DQSDelay0 = pDCTstat->DQSDelay;
+
+- DQSDelay = DQSDelay * (~scale);
++ pDCTstat->ByteLane = (like >> 8) & 0xff;
++ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
++ DQSDelay1 = pDCTstat->DQSDelay;
+
+- DQSDelay += 0x80; /* round it */
++ if (DQSDelay0>DQSDelay1) {
++ DQSDelay = DQSDelay0 - DQSDelay1;
++ } else {
++ DQSDelay = DQSDelay1 - DQSDelay0;
++ }
+
+- DQSDelay >>= 8; /* 256 */
++ DQSDelay = DQSDelay * (~scale);
++
++ DQSDelay += 0x80; /* round it */
++
++ DQSDelay >>= 8; /* 256 */
+
+- if (DQSDelay0>DQSDelay1) {
+- DQSDelay = DQSDelay1 - DQSDelay;
+- } else {
+- DQSDelay += DQSDelay1;
++ if (DQSDelay0>DQSDelay1) {
++ DQSDelay = DQSDelay1 - DQSDelay;
++ } else {
++ DQSDelay += DQSDelay1;
++ }
+ }
+
+ pDCTstat->DQSDelay = (u8)DQSDelay;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+index 5ea7fa6..9617f84 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+@@ -18,12 +18,39 @@
+ * Foundation, Inc.
+ */
+
++static uint16_t memclk_to_freq(uint16_t memclk) {
++ uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++
++ uint16_t mem_freq = 0;
++
++ if (is_fam15h()) {
++ if (memclk < 0x17) {
++ mem_freq = fam15h_freq_tab[memclk];
++ }
++ } else {
++ if ((memclk > 0x0) && (memclk < 0x8)) {
++ mem_freq = fam10h_freq_tab[memclk - 1];
++ }
++ }
++
++ return mem_freq;
++}
++
++static uint32_t rc_word_value_to_ctl_bits(uint32_t value) {
++ return ((value >> 2) & 3) << 16 | ((value & 3) << 3);
++}
++
+ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 MrsChipSel, u32 CtrlWordNum)
+ {
+- u8 Dimms, DimmNum, MaxDimm, Speed;
++ u8 Dimms, DimmNum;
+ u32 val;
+ u32 dct = 0;
++ uint8_t ddr_voltage_index;
++ uint16_t mem_freq;
++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ DimmNum = (MrsChipSel >> 20) & 0xFE;
+
+@@ -32,54 +59,64 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ /* DimmNum ++; */
+ /* cl +=8; */
+
+- MaxDimm = mctGet_NVbits(NV_MAX_DIMMS);
+- Speed = pDCTstat->DIMMAutoSpeed;
++ mem_freq = memclk_to_freq(pDCTstat->DIMMAutoSpeed);
+
+ if (pDCTstat->CSPresent_DCT[0] > 0) {
+ dct = 0;
+- } else if (pDCTstat->CSPresent_DCT[1] > 0 ){
++ } else if (pDCTstat->CSPresent_DCT[1] > 0 ) {
+ dct = 1;
+- DimmNum ++;
++ DimmNum++;
+ }
+ Dimms = pDCTstat->MAdimms[dct];
+
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++
+ val = 0;
+ if (CtrlWordNum == 0)
+- val |= 1 << 1;
++ val = 0x2;
+ else if (CtrlWordNum == 1) {
+ if (!((pDCTstat->DimmDRPresent | pDCTstat->DimmQRPresent) & (1 << DimmNum)))
+- val |= 0xC; /* if single rank, set DBA1 and DBA0 */
++ val = 0xC; /* if single rank, set DBA1 and DBA0 */
+ } else if (CtrlWordNum == 2) {
+- if (MaxDimm == 4) {
+- if (Speed == 4) {
+- if (((pDCTstat->DimmQRPresent & (1 << DimmNum)) && (Dimms == 1)) || (Dimms == 2))
+- if (!(pDCTstat->MirrPresU_NumRegR & (1 << DimmNum)))
+- val |= 1 << 2;
+- } else {
+- if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum))
+- val |= 1 << 2;
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (MaxDimmsInstallable == 2) {
++ if (Dimms > 1)
++ val = 0x4;
+ }
+- } else {
+- if (Dimms > 1)
+- val |= 1 << 2;
+ }
+ } else if (CtrlWordNum == 3) {
+- val |= (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xFF;
+ } else if (CtrlWordNum == 4) {
+- val |= (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xFF;
+ } else if (CtrlWordNum == 5) {
+- val |= (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xFF;
+ } else if (CtrlWordNum == 8) {
+- if (MaxDimm == 4)
+- if (Speed == 4)
+- if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum))
+- val |= 1 << 2;
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (MaxDimmsInstallable == 2) {
++ val = 0x0;
++ }
++ }
+ } else if (CtrlWordNum == 9) {
+- val |= 0xD; /* DBA1, DBA0, DA3 = 0 */
++ val = 0xD; /* DBA1, DBA0, DA3 = 0 */
++ } else if (CtrlWordNum == 10) {
++ val = 0x0; /* Lowest operating frequency */
++ } else if (CtrlWordNum == 11) {
++ if (ddr_voltage_index & 0x4)
++ val = 0x2; /* 1.25V */
++ else if (ddr_voltage_index & 0x2)
++ val = 0x1; /* 1.35V */
++ else
++ val = 0x0; /* 1.5V */
++ } else if (CtrlWordNum >= 12) {
++ val = 0x0; /* Unset */
+ }
+- val &= 0xffffff0f;
++ val &= 0xf;
++
++ printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", CtrlWordNum, val);
+
+- val = MrsChipSel | ((val >> 2) & 3) << 16 | ((val & 3) << 3);
++ val = MrsChipSel | rc_word_value_to_ctl_bits(val);
+
+ /* transfer Control word number to address [BA2,A2,A1,A0] */
+ if (CtrlWordNum > 7) {
+@@ -129,18 +166,18 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ val &= ~(0xF << 8);
+
+ switch (MrsChipSel) {
+- case 0:
+- case 1:
+- val |= 3 << 8;
+- case 2:
+- case 3:
+- val |= (3 << 2) << 8;
+- case 4:
+- case 5:
+- val |= (3 << 4) << 8;
+- case 6:
+- case 7:
+- val |= (3 << 6) << 8;
++ case 0:
++ case 1:
++ val |= 3 << 8;
++ case 2:
++ case 3:
++ val |= (3 << 2) << 8;
++ case 4:
++ case 5:
++ val |= (3 << 4) << 8;
++ case 6:
++ case 7:
++ val |= (3 << 6) << 8;
+ }
+ Set_NB32_DCT(dev, dct, 0xa8, val);
+
+@@ -164,8 +201,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ u32 MrsChipSel;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
++ uint16_t mem_freq;
+
+ pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
++ mem_freq = memclk_to_freq(pDCTstat->TargetFreq);
+ for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
+@@ -175,19 +214,31 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */
+
+ /* Resend control word 10 */
++ uint8_t freq_ctl_val = 0;
+ mct_Wait(1600);
+- switch (pDCTstat->TargetFreq) {
+- case 5:
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4000A);
+- break;
+- case 6:
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40012);
+- break;
+- case 7:
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4001A);
+- break;
++ switch (mem_freq) {
++ case 333:
++ case 400:
++ freq_ctl_val = 0x0;
++ break;
++ case 533:
++ freq_ctl_val = 0x1;
++ break;
++ case 667:
++ freq_ctl_val = 0x2;
++ break;
++ case 800:
++ freq_ctl_val = 0x3;
++ break;
++ case 933:
++ freq_ctl_val = 0x4;
++ break;
+ }
+
++ printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", 10, freq_ctl_val);
++
++ mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40002 | rc_word_value_to_ctl_bits(freq_ctl_val));
++
+ mct_Wait(1600);
+
+ /* Resend control word 2 */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 6a2c2a7..9ccf77e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -496,6 +496,8 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+ ret |= ((dword >> 10) & 3) << 9;
+ }
+
++ printk(BIOS_SPEW, "Going to send MR2 control word %08x\n", ret);
++
+ return ret;
+ }
+
+@@ -525,6 +527,8 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
+ ret |= (dword >> 24) & 7;
+ }
+
++ printk(BIOS_SPEW, "Going to send MR3 control word %08x\n", ret);
++
+ return ret;
+ }
+
+@@ -619,6 +623,8 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ ret |= 1 << 12;
+ }
+
++ printk(BIOS_SPEW, "Going to send MR1 control word %08x\n", ret);
++
+ return ret;
+ }
+
+@@ -738,6 +744,8 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
+ ret |= 1 << 8;
+ }
+
++ printk(BIOS_SPEW, "Going to send MR0 control word %08x\n", ret);
++
+ return ret;
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 3655e84..abb5321 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -1745,6 +1745,7 @@ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ u16 EccDQSLike;
+ u8 EccDQSScale;
+ u32 val, val0, val1;
++ int16_t delay_differential;
+
+ EccDQSLike = pDCTstat->CH_EccDQSLike[Channel];
+ EccDQSScale = pDCTstat->CH_EccDQSScale[Channel];
+@@ -1754,14 +1755,22 @@ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ u16 *p;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][ChipSel>>1];
+
+- /* DQS Delay Value of Data Bytelane
+- * most like ECC byte lane */
+- val0 = p[EccDQSLike & 0x07];
+- /* DQS Delay Value of Data Bytelane
+- * 2nd most like ECC byte lane */
+- val1 = p[(EccDQSLike>>8) & 0x07];
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ val0 = p[0x2];
++ val1 = p[0x3];
++
++ delay_differential = (int16_t)val1 - (int16_t)val0;
++ delay_differential += (int16_t)val1;
++
++ val = delay_differential;
++ } else {
++ /* DQS Delay Value of Data Bytelane
++ * most like ECC byte lane */
++ val0 = p[EccDQSLike & 0x07];
++ /* DQS Delay Value of Data Bytelane
++ * 2nd most like ECC byte lane */
++ val1 = p[(EccDQSLike>>8) & 0x07];
+
+- if (!(pDCTstat->Status & (1 << SB_Registered))) {
+ if(val0 > val1) {
+ val = val0 - val1;
+ } else {
+@@ -1776,9 +1785,6 @@ static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ } else {
+ val += val0;
+ }
+- } else {
+- val = val1 - val0;
+- val += val1;
+ }
+
+ pDCTstat->CH_D_BC_RCVRDLY[Channel][ChipSel>>1] = val;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 26e1374..fb3b3d8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -930,7 +930,9 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ else if ((cs == 4) || (cs == 0))
+ WrLvOdt1 = (dword & 0xf);
+ } else {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
++ } else {
+ if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
+ WrLvOdt1 = 0x03;
+ } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
+@@ -938,14 +940,14 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ } else {
+ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
+ }
+- } else {
+- WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
+ }
+ }
+
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
+
++ printk(BIOS_SPEW, "Programmed DCT %d write levelling ODT pattern %08x\n", dct, WrLvOdt1);
++
+ }
+
+ #ifdef UNUSED_CODE
+@@ -980,7 +982,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ u16 Addl_Data_Offset, Addl_Data_Port;
+ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+- u16 fam10h_freq_tab[] = {400, 533, 667, 800};
++ uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
+ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
+ if (is_fam15h()) {
+@@ -1093,21 +1095,18 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+ }
+ } else {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- {
+- if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct))))
+- {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++
++ /* The seed values below assume Pass 1 utilizes a 400MHz clock frequency (DDR3-800) */
++ if (AddrCmdPrelaunch == 0) {
+ Seed_Gross = 0x02;
+- Seed_Fine = 0x16;
+- }
+- else
+- {
++ Seed_Fine = 0x01;
++ } else {
+ Seed_Gross = 0x02;
+- Seed_Fine = 0x00;
++ Seed_Fine = 0x11;
+ }
+- }
+- else
+- {
++ } else {
+ if (MemClkFreq == 6) {
+ /* DDR-800 */
+ Seed_Gross = 0x00;
+@@ -1131,6 +1130,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ */
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ printk(BIOS_SPEW, "\tLane %02x initial seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+ }
+ } else {
+ /* Pass 2 */
+@@ -1182,21 +1182,30 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+ }
+ } else {
+- u32 RegisterDelay, SeedTotal;
++ uint32_t RegisterDelay;
++ uint32_t SeedTotalPreScaling;
++ uint32_t SeedTotal;
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+ {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED])
+- RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
+- else
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (AddrCmdPrelaunch == 0)
++ RegisterDelay = 0x20;
++ else
++ RegisterDelay = 0x30;
++ } else {
+ RegisterDelay = 0;
+- SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+- (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
++ }
++ SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5)) - RegisterDelay;
+ /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
+ training) - RegisterDelay. */
+- SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
+- fam10h_freq_tab[MemClkFreq-3] * 100) / (fam10h_freq_tab[0] * 100)));
++ SeedTotal = (uint16_t) ((((uint64_t) SeedTotalPreScaling) *
++ fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100));
+ Seed_Gross = SeedTotal / 32;
+ Seed_Fine = SeedTotal & 0x1f;
+ if (Seed_Gross == 0)
+@@ -1205,8 +1214,20 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ Seed_Gross = 1;
+ else
+ Seed_Gross = 2;
++
++ /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
++ * due to the long register delays causing premature total delay wrap-around.
++ * Attempt to work around this...
++ */
++ SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f);
++ SeedTotal += RegisterDelay;
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal & 0x1f;
++
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+ }
+ }
+ }
+@@ -1383,6 +1404,8 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
+ gross = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId,
+ FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc);
+
++ printk(BIOS_SPEW, "\tLane %02x raw readback: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f));
++
+ if (!is_fam15h()) {
+ /* Adjust seed gross delay overflow (greater than 3):
+ * - Adjust the trained gross delay to the original seed gross delay.
+@@ -1406,4 +1429,5 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
+ }
+ pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
+ pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
++ printk(BIOS_SPEW, "\tLane %02x final adjusted value: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f));
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0087-northbridge-amd-amdmct-Fix-Family-15h-detection.patch b/resources/libreboot/patch/kgpe-d16/0087-northbridge-amd-amdmct-Fix-Family-15h-detection.patch
new file mode 100644
index 00000000..3c09ca62
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0087-northbridge-amd-amdmct-Fix-Family-15h-detection.patch
@@ -0,0 +1,47 @@
+From 82ac366ef8d8c624af7038cc5bfa58c8c5a3486c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 28 Jul 2015 13:45:29 -0500
+Subject: [PATCH 087/146] northbridge/amd/amdmct: Fix Family 15h detection
+
+---
+ src/northbridge/amd/amdmct/mct/mct.h | 3 ++-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct/mct.h b/src/northbridge/amd/amdmct/mct/mct.h
+index d399eea..1af11b1 100644
+--- a/src/northbridge/amd/amdmct/mct/mct.h
++++ b/src/northbridge/amd/amdmct/mct/mct.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -501,7 +502,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ u32 NodePresent(u32 Node);
+ u32 Get_NB32n(struct DCTStatStruc *pDCTstat, u32 addrx);
+ u32 Get_NB32(u32 addr); /* NOTE: extend addr to 32 bit for bus > 0 */
+-u32 mctGetLogicalCPUID(u32 Node);
++uint64_t mctGetLogicalCPUID(u32 Node);
+
+ void K8FInterleaveBanks(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 6b5d8c1..e327d38 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -985,7 +985,7 @@ u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass);
+ u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u16 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
+ void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+-u32 mctGetLogicalCPUID(u32 Node);
++uint64_t mctGetLogicalCPUID(u32 Node);
+ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u8 Pass);
+ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0088-northbridge-amd-amdmct-mct_ddr3-Add-registered-and-x.patch b/resources/libreboot/patch/kgpe-d16/0088-northbridge-amd-amdmct-mct_ddr3-Add-registered-and-x.patch
new file mode 100644
index 00000000..0aba7eb5
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0088-northbridge-amd-amdmct-mct_ddr3-Add-registered-and-x.patch
@@ -0,0 +1,1893 @@
+From 46e87f3f6b6085488fa6eb8b5ad69ba2efdc3e4f Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 28 Jul 2015 15:16:46 -0500
+Subject: [PATCH 088/146] northbridge/amd/amdmct/mct_ddr3: Add registered and
+ x4 DIMM support to Fam15h
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 186 ++++++-----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 2 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 4 +
+ src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c | 17 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 191 +++++++----
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 42 ++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 267 +++++++++-------
+ src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 16 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 402 +++++++++++++++---------
+ src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h | 13 +-
+ 10 files changed, 706 insertions(+), 434 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 28796bb..55cdd24 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -166,7 +166,7 @@ static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat);
++ struct DCTStatStruc *pDCTstatA, uint8_t Node);
+
+ static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel);
+@@ -1404,6 +1404,10 @@ static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCT
+
+ memclk_freq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
++ if (fam15h_freq_tab[memclk_freq] == 0) {
++ printk(BIOS_DEBUG, "ERROR: precise_memclk_delay_fam15 for DCT %d (delay %d clocks) failed to obtain valid memory frequency!"
++ " (pDCTstat: %p pDCTstat->dev_dct: %08x memclk_freq: %02x)\n", dct, clocks, pDCTstat, pDCTstat->dev_dct, memclk_freq);
++ }
+ delay_ns = (((uint64_t)clocks * 1000) / fam15h_freq_tab[memclk_freq]);
+ precise_ndelay_fam15(pMCTstat, delay_ns);
+ }
+@@ -2315,7 +2319,7 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ nv_DQSTrainCTL = !allow_config_restore;
+
+ mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
+- phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
++ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA, -1);
+
+ if (is_fam15h()) {
+ uint8_t Node;
+@@ -3350,7 +3354,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ }
+
+ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ /* Initialize DCT Timing registers as per DIMM SPD.
+ * For primary timing (T, CL) use best case T value.
+@@ -3454,7 +3458,7 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ /* Find the best T and CL primary timing parameter pair, per Mfg.,
+ * for the given set of DIMMs, and store into DCTStatStruc
+@@ -3733,10 +3737,15 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ dword++;
+ }
+
+- if (Status & (1 << SB_Registered))
+- DramConfigLo |= 1 << ParEn; /* Registered DIMMs */
+- else
+- DramConfigLo |= 1 << UnBuffDimm; /* Unbuffered DIMMs */
++ if (Status & (1 << SB_Registered)) {
++ /* Registered DIMMs */
++ if (!is_fam15h()) {
++ DramConfigLo |= 1 << ParEn;
++ }
++ } else {
++ /* Unbuffered DIMMs */
++ DramConfigLo |= 1 << UnBuffDimm;
++ }
+
+ if (mctGet_NVbits(NV_ECC_CAP))
+ if (Status & (1 << SB_ECCDIMMs))
+@@ -3754,10 +3763,11 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ DramConfigHi |= dword - offset; /* get MemClk encoding */
+ DramConfigHi |= 1 << MemClkFreqVal;
+
+- if (Status & (1 << SB_Registered))
+- if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
+- /* set only if x8 Registered DIMMs in System*/
+- DramConfigHi |= 1 << RDqsEn;
++ if (!is_fam15h())
++ if (Status & (1 << SB_Registered))
++ if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
++ /* set only if x8 Registered DIMMs in System*/
++ DramConfigHi |= 1 << RDqsEn;
+
+ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
+ DramConfigLo |= 1 << 25; /* PendRefPaybackS3En = 1 */
+@@ -3769,14 +3779,16 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ DramConfigHi |= 1 << 16;
+ }
+
+- /* Control Bank Swizzle */
+- if (0) /* call back not needed mctBankSwizzleControl_D()) */
+- DramConfigHi &= ~(1 << BankSwizzleMode);
+- else
+- DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */
++ if (!is_fam15h()) {
++ /* Control Bank Swizzle */
++ if (0) /* call back not needed mctBankSwizzleControl_D()) */
++ DramConfigHi &= ~(1 << BankSwizzleMode);
++ else
++ DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */
++ }
+
+ /* Check for Quadrank DIMM presence */
+- if ( pDCTstat->DimmQRPresent != 0) {
++ if (pDCTstat->DimmQRPresent != 0) {
+ byte = mctGet_NVbits(NV_4RANKType);
+ if (byte == 2)
+ DramConfigHi |= 1 << 17; /* S4 (4-Rank SO-DIMMs) */
+@@ -4577,8 +4589,9 @@ static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
+ Set_NB32(pDCTstat->dev_dct, reg, val);
+ }
+ if (byte) /* NV_Unganged */
+- pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM missmatch error */
++ pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM mismatch error */
+ }
++
+ return pDCTstat->ErrCode;
+ }
+
+@@ -4639,6 +4652,8 @@ void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data)
+ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ /* mct_checkForCxDxSupport_D */
+ if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) {
+ /* Family 10h Errata 322: Address and Command Fine Delay Values May Be Incorrect */
+@@ -4653,6 +4668,9 @@ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
+ else
+ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x90);
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
++
+ return pDCTstat->ErrCode;
+ }
+
+@@ -4663,6 +4681,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ * and program them into DCT.
+ */
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg;
+ u8 i, i_start, i_end;
+@@ -4683,6 +4703,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ printk(BIOS_SPEW, "Programmed DCT %d timing/termination pattern %08x %08x\n", dct, pDCTstat->CH_ADDR_TMG[i], pDCTstat->CH_ODC_CTL[i]);
+ }
+
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
++
+ return pDCTstat->ErrCode;
+ }
+
+@@ -4694,7 +4716,8 @@ static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat)
+ if (pDCTstat->NodePresent) {
+ dev = pDCTstat->dev_dct;
+
+- if ((pDCTstat->DIMMValidDCT[0] ) || (pDCTstat->DIMMValidDCT[1])) { /* This Node has dram */
++ if ((pDCTstat->DIMMValidDCT[0]) || (pDCTstat->DIMMValidDCT[1])) {
++ /* This Node has DRAM */
+ do {
+ val = Get_NB32(dev, 0x110);
+ } while (!(val & (1 << DramEnabled)));
+@@ -5642,57 +5665,56 @@ static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
+ /* Fam15h BKDG v3.14 section 2.10.5.3
+ * The remainder of the Phy Initialization algorithm picks up in phyAssistedMemFnceTraining
+ */
+- for (dct = 0; dct < 2; dct++) {
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
+-
+- /* Program desired VDDIO level */
+- if (ddr_voltage_index & 0x4) {
+- /* 1.25V */
+- amd_voltage_level_index = 0x2;
+- } else if (ddr_voltage_index & 0x2) {
+- /* 1.35V */
+- amd_voltage_level_index = 0x1;
+- } else if (ddr_voltage_index & 0x1) {
+- /* 1.50V */
+- amd_voltage_level_index = 0x0;
+- }
+-
+- /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
+- for (index = 0; index < 0x9; index++) {
+- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
+- dword &= ~(0x3 << 3);
+- dword |= (amd_voltage_level_index << 3);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
+- }
+-
+- /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
+- for (index = 0; index < 0x3; index++) {
+- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
+- dword &= ~(0x3 << 3);
+- dword |= (amd_voltage_level_index << 3);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
+- }
+- for (index = 0; index < 0x2; index++) {
+- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
+- dword &= ~(0x3 << 3);
+- dword |= (amd_voltage_level_index << 3);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
+- }
+- for (index = 0; index < 0x1; index++) {
+- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
+- dword &= ~(0x3 << 3);
+- dword |= (amd_voltage_level_index << 3);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
+- }
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
+
+- /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
+- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009);
+- dword &= ~(0x0000c00c);
+- dword |= (amd_voltage_level_index << 14);
+- dword |= (amd_voltage_level_index << 2);
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009, dword);
+- }
++ /* Program desired VDDIO level */
++ if (ddr_voltage_index & 0x4) {
++ /* 1.25V */
++ amd_voltage_level_index = 0x2;
++ } else if (ddr_voltage_index & 0x2) {
++ /* 1.35V */
++ amd_voltage_level_index = 0x1;
++ } else if (ddr_voltage_index & 0x1) {
++ /* 1.50V */
++ amd_voltage_level_index = 0x0;
++ }
++
++ /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
++ }
++
++ /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
++ for (index = 0; index < 0x3; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
++ }
++ for (index = 0; index < 0x2; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
++ }
++ for (index = 0; index < 0x1; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
++ dword &= ~(0x3 << 3);
++ dword |= (amd_voltage_level_index << 3);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
++ }
++
++ /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
++ /* NOTE: CmpVioLvl and ComparatorAdjust only take effect when set on DCT 0 */
++ dword = Get_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0f4009);
++ dword &= ~(0x0000c00c);
++ dword |= (amd_voltage_level_index << 14);
++ dword |= (amd_voltage_level_index << 2);
++ Set_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0f4009, dword);
+
+ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+@@ -5708,18 +5730,24 @@ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
+ uint32_t dword;
+ const u8 *p;
+
+- printk(BIOS_DEBUG, "%s: Start\n", __func__);
++ printk(BIOS_DEBUG, "%s: DCT %d: Start\n", __func__, dct);
+
+ if (is_fam15h()) {
+ /* Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 */
+ uint32_t tx_pre;
+ uint32_t drive_strength;
+
+- /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp] */
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
+- dword |= (0x3 << 13);
++ dword |= (0x1 << 14);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
+
++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisablePredriverCal] */
++ /* NOTE: DisablePredriverCal only takes effect when set on DCT 0 */
++ dword = Get_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0fe003);
++ dword |= (0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0fe003, dword);
++
+ /* Determine TxPreP/TxPreN for data lanes (Stage 1) */
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+ drive_strength = (dword >> 20) & 0x7; /* DqsDrvStren */
+@@ -5865,12 +5893,14 @@ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0a, dword);
+ }
+
+- printk(BIOS_DEBUG, "%s: Done\n", __func__);
++ printk(BIOS_DEBUG, "%s: DCT %d: Done\n", __func__, dct);
+ }
+
+ static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ if (!is_fam15h()) {
+ u32 reg;
+ u32 val;
+@@ -5892,6 +5922,8 @@ static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+
+ Set_NB32_DCT(dev, dct, reg, val);
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+@@ -6535,6 +6567,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
+
+ uint32_t dword;
+
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ if (is_fam15h()) {
+ /* Initial setup for frequency change
+ * 9C_x0000_0004 must be configured before MemClkFreqVal is set
+@@ -6567,6 +6601,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
+ mct_Wait(100);
+ }
+
++ printk(BIOS_DEBUG, "mct_SetDramConfigHi_D: DramConfigHi: %08x\n", DramConfigHi);
++
+ /* Program the DRAM Configuration High register */
+ Set_NB32_DCT(dev, dct, 0x94, DramConfigHi);
+
+@@ -6582,6 +6618,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
+ dword |= 0x0000000f;
+ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index e327d38..486b16c 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -1014,7 +1014,7 @@ void InterleaveNodes_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTs
+ void InterleaveChannels_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+-void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
++void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, int16_t Node);
+ u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass);
+ u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct);
+ void mct_Wait(u32 cycles);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 41b7244..bc9ac4b 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -1588,6 +1588,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
+
+ for (dct = 0; dct < 2; dct++) {
+ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
++ /* NOTE: DisablePredriverCal only takes effect when set on DCT 0 */
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
+ dword &= ~(0x3 << 13);
+ dword |= (0x1 << 13);
+@@ -1627,6 +1628,9 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
+ rx_en_offset = (initial_phy_phase_delay[lane] + 0x10) % 0x40;
+
+ /* 2.10.5.8.3 (4) */
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 Receiver %d lane %d initial phy delay %04x: iterating from %04x to %04x\n", Receiver, lane, initial_phy_phase_delay[lane], rx_en_offset, 0x3ff);
++#endif
+ for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
+ /* 2.10.5.8.3 (4 A) */
+ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+index 539cb0d..1b81d15 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+@@ -21,7 +21,7 @@
+ static uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+ static uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass);
+ static uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+ static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+@@ -133,7 +133,7 @@ static uint8_t PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ }
+
+ static uint8_t PhyWLPass2(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t final)
+ {
+ u8 dimm;
+ u16 DIMMValid;
+@@ -187,12 +187,15 @@ static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
+ * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1
+ */
+ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, uint8_t Pass)
++ struct DCTStatStruc *pDCTstatA, uint8_t Node, uint8_t Pass)
+ {
+ uint8_t status;
+ uint8_t timeout;
+ uint16_t final_target_freq;
+
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
+ pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
+ pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
+ pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
+@@ -240,13 +243,13 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed);
+ else
+ pDCTstat->TargetFreq = final_target_freq;
+- SetTargetFreq(pMCTstat, pDCTstat);
++ SetTargetFreq(pMCTstat, pDCTstatA, Node);
+ timeout = 0;
+ do {
+ status = 0;
+ timeout++;
+- status |= PhyWLPass2(pMCTstat, pDCTstat, 0);
+- status |= PhyWLPass2(pMCTstat, pDCTstat, 1);
++ status |= PhyWLPass2(pMCTstat, pDCTstat, 0, (pDCTstat->TargetFreq == final_target_freq));
++ status |= PhyWLPass2(pMCTstat, pDCTstat, 1, (pDCTstat->TargetFreq == final_target_freq));
+ if (status)
+ printk(BIOS_INFO,
+ "%s: Retrying write levelling due to invalid value(s) detected in last phase\n",
+@@ -290,7 +293,7 @@ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->NodePresent) {
+ mctSMBhub_Init(Node);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+- WriteLevelization_HW(pMCTstat, pDCTstat, Pass);
++ WriteLevelization_HW(pMCTstat, pDCTstatA, Node, Pass);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+index 9617f84..624a543 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+@@ -18,6 +18,78 @@
+ * Foundation, Inc.
+ */
+
++static uint8_t fam15h_rdimm_rc2_control_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
++
++ uint8_t package_type;
++ uint8_t control_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++
++ /* FIXME
++ * Assume there is only one register on the RDIMM for now
++ */
++ uint8_t num_registers = 1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.7.1.2.1 Table 85 */
++ if (MaxDimmsInstallable == 1) {
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ control_code = 0x1;
++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
++ /* DDR3-1066 - DDR3-1333 */
++ if (num_registers == 1) {
++ control_code = 0x0;
++ } else {
++ control_code = 0x1;
++ }
++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
++ /* DDR3-1600 - DDR3-1866 */
++ control_code = 0x0;
++ }
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
++ /* DDR3-667 - DDR3-800 */
++ control_code = 0x1;
++ } else if ((MemClkFreq >= 0xa) && (MemClkFreq <= 0x12)) {
++ /* DDR3-1066 - DDR3-1600 */
++ if (num_registers == 1) {
++ control_code = 0x0;
++ } else {
++ control_code = 0x1;
++ }
++ }
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ if (num_registers == 1) {
++ control_code = 0x1;
++ } else {
++ control_code = 0x8;
++ }
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return control_code;
++}
++
+ static uint16_t memclk_to_freq(uint16_t memclk) {
+ uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
+ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+@@ -37,36 +109,46 @@ static uint16_t memclk_to_freq(uint16_t memclk) {
+ return mem_freq;
+ }
+
++static uint8_t rc_word_chip_select_lower_bit(void) {
++ if (is_fam15h()) {
++ return 21;
++ } else {
++ return 20;
++ }
++}
++
++static uint32_t rc_word_address_to_ctl_bits(uint32_t address) {
++ if (is_fam15h()) {
++ return (((address >> 3) & 0x1) << 2) << 18 | (address & 0x7);
++ } else {
++ return (((address >> 3) & 0x1) << 2) << 16 | (address & 0x7);
++ }
++}
++
+ static uint32_t rc_word_value_to_ctl_bits(uint32_t value) {
+- return ((value >> 2) & 3) << 16 | ((value & 3) << 3);
++ if (is_fam15h()) {
++ return ((value >> 2) & 0x3) << 18 | ((value & 0x3) << 3);
++ } else {
++ return ((value >> 2) & 0x3) << 16 | ((value & 0x3) << 3);
++ }
+ }
+
+ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u32 MrsChipSel, u32 CtrlWordNum)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MrsChipSel, u32 CtrlWordNum)
+ {
+ u8 Dimms, DimmNum;
+ u32 val;
+- u32 dct = 0;
+ uint8_t ddr_voltage_index;
+ uint16_t mem_freq;
+ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+- DimmNum = (MrsChipSel >> 20) & 0xFE;
++ DimmNum = (MrsChipSel >> rc_word_chip_select_lower_bit()) & 0xfe;
+
+- /* assume dct=0; */
+- /* if (dct == 1) */
+- /* DimmNum ++; */
+- /* cl +=8; */
++ if (dct == 1)
++ DimmNum++;
+
+ mem_freq = memclk_to_freq(pDCTstat->DIMMAutoSpeed);
+-
+- if (pDCTstat->CSPresent_DCT[0] > 0) {
+- dct = 0;
+- } else if (pDCTstat->CSPresent_DCT[1] > 0 ) {
+- dct = 1;
+- DimmNum++;
+- }
+ Dimms = pDCTstat->MAdimms[dct];
+
+ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
+@@ -76,21 +158,25 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ val = 0x2;
+ else if (CtrlWordNum == 1) {
+ if (!((pDCTstat->DimmDRPresent | pDCTstat->DimmQRPresent) & (1 << DimmNum)))
+- val = 0xC; /* if single rank, set DBA1 and DBA0 */
++ val = 0xc; /* if single rank, set DBA1 and DBA0 */
+ } else if (CtrlWordNum == 2) {
+- if (package_type == PT_GR) {
+- /* Socket G34 */
+- if (MaxDimmsInstallable == 2) {
+- if (Dimms > 1)
+- val = 0x4;
++ if (is_fam15h()) {
++ val = fam15h_rdimm_rc2_control_code(pDCTstat, dct);
++ } else {
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (MaxDimmsInstallable == 2) {
++ if (Dimms > 1)
++ val = 0x4;
++ }
+ }
+ }
+ } else if (CtrlWordNum == 3) {
+- val = (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xff;
+ } else if (CtrlWordNum == 4) {
+- val = (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xff;
+ } else if (CtrlWordNum == 5) {
+- val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xFF;
++ val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xff;
+ } else if (CtrlWordNum == 8) {
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+@@ -99,7 +185,7 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ }
+ }
+ } else if (CtrlWordNum == 9) {
+- val = 0xD; /* DBA1, DBA0, DA3 = 0 */
++ val = 0xd; /* DBA1, DBA0, DA3 = 0 */
+ } else if (CtrlWordNum == 10) {
+ val = 0x0; /* Lowest operating frequency */
+ } else if (CtrlWordNum == 11) {
+@@ -114,43 +200,30 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ }
+ val &= 0xf;
+
+- printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", CtrlWordNum, val);
++ printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, CtrlWordNum, val);
+
+ val = MrsChipSel | rc_word_value_to_ctl_bits(val);
+-
+- /* transfer Control word number to address [BA2,A2,A1,A0] */
+- if (CtrlWordNum > 7) {
+- val |= 1 << 18;
+- CtrlWordNum &= 7;
+- }
+- val |= CtrlWordNum;
++ val |= rc_word_address_to_ctl_bits(CtrlWordNum);
+
+ return val;
+ }
+
+ static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u32 val)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t val)
+ {
+- uint8_t dct = 0;
+ u32 dev = pDCTstat->dev_dct;
+
+- if (pDCTstat->CSPresent_DCT[0] > 0) {
+- dct = 0;
+- } else if (pDCTstat->CSPresent_DCT[1] > 0 ){
+- dct = 1;
+- }
+-
+- val |= Get_NB32_DCT(dev, dct, 0x7C) & ~0xFFFFFF;
++ val |= Get_NB32_DCT(dev, dct, 0x7c) & ~0xffffff;
+ val |= 1 << SendControlWord;
+- Set_NB32_DCT(dev, dct, 0x7C, val);
++ Set_NB32_DCT(dev, dct, 0x7c, val);
+
+ do {
+- val = Get_NB32_DCT(dev, dct, 0x7C);
++ val = Get_NB32_DCT(dev, dct, 0x7c);
+ } while (val & (1 << SendControlWord));
+ }
+
+ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ u8 MrsChipSel;
+ u32 dev = pDCTstat->dev_dct;
+@@ -163,7 +236,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ val = Get_NB32_DCT(dev, dct, 0xa8);
+- val &= ~(0xF << 8);
++ val &= ~(0xf << 8);
+
+ switch (MrsChipSel) {
+ case 0:
+@@ -184,8 +257,8 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ for (cw=0; cw <=15; cw ++) {
+ mct_Wait(1600);
+ if (!(cw==6 || cw==7)) {
+- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, cw);
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
++ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), cw);
++ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
+ }
+ }
+ }
+@@ -195,7 +268,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat)
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ u32 SaveSpeed = pDCTstat->DIMMAutoSpeed;
+ u32 MrsChipSel;
+@@ -208,10 +281,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
+- val = Get_NB32_DCT(dev, 0, 0xA8); /* TODO: dct 0 / 1 select */
+- val &= ~(0xFF << 8);
+- val |= (0x3 << (MrsChipSel & 0xFE)) << 8;
+- Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */
++ val = Get_NB32_DCT(dev, dct, 0xa8);
++ val &= ~(0xff << 8);
++ val |= (0x3 << (MrsChipSel & 0xfe)) << 8;
++ Set_NB32_DCT(dev, dct, 0xa8, val);
+
+ /* Resend control word 10 */
+ uint8_t freq_ctl_val = 0;
+@@ -235,21 +308,21 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ break;
+ }
+
+- printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", 10, freq_ctl_val);
++ printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, 10, freq_ctl_val);
+
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40002 | rc_word_value_to_ctl_bits(freq_ctl_val));
++ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit() | rc_word_address_to_ctl_bits(10) | rc_word_value_to_ctl_bits(freq_ctl_val));
+
+ mct_Wait(1600);
+
+ /* Resend control word 2 */
+- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 2);
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
++ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 2);
++ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
+
+ mct_Wait(1600);
+
+ /* Resend control word 8 */
+- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 8);
+- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
++ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 8);
++ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
+
+ mct_Wait(1600);
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 9ccf77e..09a5f68 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -445,13 +445,13 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
++
+ if (is_fam15h()) {
+ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+- /* The formula for chip select number is: CS = dimm*2+rank */
+- uint8_t dimm = MrsChipSel / 2;
+- uint8_t rank = MrsChipSel % 2;
+-
+ /* FIXME: These parameters should be configurable
+ * For now, err on the side of caution and enable automatic 2x refresh
+ * when the DDR temperature rises above the internal limits
+@@ -496,7 +496,7 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
+ ret |= ((dword >> 10) & 3) << 9;
+ }
+
+- printk(BIOS_SPEW, "Going to send MR2 control word %08x\n", ret);
++ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR2 control word %08x\n", dct, dimm, rank, ret);
+
+ return ret;
+ }
+@@ -507,6 +507,10 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
++
+ if (is_fam15h()) {
+ ret = 0xc0000;
+ ret |= (MrsChipSel << 21);
+@@ -527,7 +531,7 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
+ ret |= (dword >> 24) & 7;
+ }
+
+- printk(BIOS_SPEW, "Going to send MR3 control word %08x\n", ret);
++ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR3 control word %08x\n", dct, dimm, rank, ret);
+
+ return ret;
+ }
+@@ -538,6 +542,10 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret;
+
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
++
+ if (is_fam15h()) {
+ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+@@ -553,10 +561,6 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ ret = 0x40000;
+ ret |= (MrsChipSel << 21);
+
+- /* The formula for chip select number is: CS = dimm*2+rank */
+- uint8_t dimm = MrsChipSel / 2;
+- uint8_t rank = MrsChipSel % 2;
+-
+ /* Determine if TQDS should be set */
+ if ((pDCTstat->Dimmx8Present & (1 << dimm))
+ && (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0)
+@@ -623,7 +627,7 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
+ ret |= 1 << 12;
+ }
+
+- printk(BIOS_SPEW, "Going to send MR1 control word %08x\n", ret);
++ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR1 control word %08x\n", dct, dimm, rank, ret);
+
+ return ret;
+ }
+@@ -634,6 +638,10 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+ u32 dword, ret, dword2;
+
++ /* The formula for chip select number is: CS = dimm*2+rank */
++ uint8_t dimm = MrsChipSel / 2;
++ uint8_t rank = MrsChipSel % 2;
++
+ if (is_fam15h()) {
+ ret = 0x00000;
+ ret |= (MrsChipSel << 21);
+@@ -744,7 +752,7 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
+ ret |= 1 << 8;
+ }
+
+- printk(BIOS_SPEW, "Going to send MR0 control word %08x\n", ret);
++ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR0 control word %08x\n", dct, dimm, rank, ret);
+
+ return ret;
+ }
+@@ -811,6 +819,16 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ /* 8.wait 360ns */
+ mct_Wait(80);
+
++ /* Set up address parity */
++ if ((pDCTstat->Status & (1 << SB_Registered))
++ || (pDCTstat->Status & (1 << SB_LoadReduced))) {
++ if (is_fam15h()) {
++ dword = Get_NB32_DCT(dev, dct, 0x90);
++ dword |= 1 << ParEn;
++ Set_NB32_DCT(dev, dct, 0x90, dword);
++ }
++ }
++
+ /* The following steps are performed with registered DIMMs only and
+ * must be done for each chip select pair */
+ if (pDCTstat->Status & (1 << SB_Registered))
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index abb5321..3af3eb2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -1147,8 +1147,10 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ uint8_t dimm;
+ uint8_t rank;
+ uint8_t lane;
++ uint8_t nibble;
+ uint8_t mem_clk;
+ uint16_t initial_seed;
++ uint8_t train_both_nibbles;
+ uint16_t current_total_delay[MAX_BYTE_LANES];
+ uint16_t dqs_ret_pass1_total_delay[MAX_BYTE_LANES];
+ uint16_t rank0_current_total_delay[MAX_BYTE_LANES];
+@@ -1164,6 +1166,11 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
+ print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
+
++ train_both_nibbles = 0;
++ if (pDCTstat->Dimmx4Present)
++ if (is_fam15h())
++ train_both_nibbles = 1;
++
+ dev = pDCTstat->dev_dct;
+ index_reg = 0x98;
+ ch_start = 0;
+@@ -1246,132 +1253,148 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ else
+ _2Ranks = 0;
+ for (rank = 0; rank < (_2Ranks + 1); rank++) {
+- /* 2.10.5.8.2 (1)
+- * Specify the target DIMM to be trained
+- * Set TrNibbleSel = 0
+- *
+- * TODO: Add support for x4 DIMMs
+- */
+- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+- dword &= ~(0x3 << 4); /* TrDimmSel */
+- dword |= ((dimm & 0x3) << 4);
+- dword &= ~(0x1 << 2); /* TrNibbleSel */
+- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+-
+- /* 2.10.5.8.2 (2)
+- * Retrieve gross and fine timing fields from write DQS registers
+- */
+- read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+-
+- /* 2.10.5.8.2.1
+- * Generate the DQS Receiver Enable Training Seed Values
+- */
+- if (Pass == FirstPass) {
+- initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
+-
+- /* Adjust seed for the minimum platform supported frequency */
+- initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
+- fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
+-
+- for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+- uint16_t wl_pass1_delay;
+- wl_pass1_delay = current_total_delay[lane];
++ for (nibble = 0; nibble < (train_both_nibbles + 1); nibble++) {
++ /* 2.10.5.8.2 (1)
++ * Specify the target DIMM and nibble to be trained
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword &= ~(0x3 << 4); /* TrDimmSel = dimm */
++ dword |= ((dimm & 0x3) << 4);
++ dword &= ~(0x1 << 2); /* TrNibbleSel = nibble */
++ dword |= ((nibble & 0x1) << 2);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++
++ /* 2.10.5.8.2 (2)
++ * Retrieve gross and fine timing fields from write DQS registers
++ */
++ read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+- seed[lane] = initial_seed + wl_pass1_delay;
+- }
+- } else {
+- uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+- uint16_t register_delay;
+- int16_t seed_prescaling;
+-
+- memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
+- if ((pDCTstat->Status & (1 << SB_Registered))) {
+- if (addr_prelaunch)
+- register_delay = 0x30;
+- else
+- register_delay = 0x20;
+- } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
+- /* TODO
+- * Load reduced DIMM support unimplemented
+- */
+- register_delay = 0x0;
++ /* 2.10.5.8.2.1
++ * Generate the DQS Receiver Enable Training Seed Values
++ */
++ if (Pass == FirstPass) {
++ initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
++
++ /* Adjust seed for the minimum platform supported frequency */
++ initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
++ fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ uint16_t wl_pass1_delay;
++ wl_pass1_delay = current_total_delay[lane];
++
++ seed[lane] = initial_seed + wl_pass1_delay;
++ }
+ } else {
+- register_delay = 0x0;
++ uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++ uint16_t register_delay;
++ int16_t seed_prescaling;
++
++ memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
++ if ((pDCTstat->Status & (1 << SB_Registered))) {
++ if (addr_prelaunch)
++ register_delay = 0x30;
++ else
++ register_delay = 0x20;
++ } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
++ /* TODO
++ * Load reduced DIMM support unimplemented
++ */
++ register_delay = 0x0;
++ } else {
++ register_delay = 0x0;
++ }
++
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ seed_prescaling = current_total_delay[lane] - register_delay - 0x20;
++ seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++ }
+ }
+
+ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+- seed_prescaling = current_total_delay[lane] - register_delay - 0x20;
+- seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
++ seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
++ seed_fine[lane] = seed[lane] & 0x1f;
++
++ /*if (seed_gross[lane] == 0)
++ seed_pre_gross[lane] = 0;
++ else */if (seed_gross[lane] & 0x1)
++ seed_pre_gross[lane] = 1;
++ else
++ seed_pre_gross[lane] = 2;
++
++ /* Calculate phase recovery delays */
++ phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
++
++ /* Set the gross delay.
++ * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
++ * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
++ */
++ current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
+ }
+- }
+-
+- for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+- seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
+- seed_fine[lane] = seed[lane] & 0x1f;
+
+- /*if (seed_gross[lane] == 0)
+- seed_pre_gross[lane] = 0;
+- else */if (seed_gross[lane] & 0x1)
+- seed_pre_gross[lane] = 1;
+- else
+- seed_pre_gross[lane] = 2;
+-
+- /* Calculate phase recovery delays */
+- phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
+-
+- /* Set the gross delay.
+- * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
+- * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
++ * Program PhRecFineDly and PhRecGrossDly
+ */
+- current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
+- }
++ write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+
+- /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
+- * Program PhRecFineDly and PhRecGrossDly
+- */
+- write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+-
+- /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7)
+- * Program the DQS Receiver Enable delay values for each lane
+- */
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7)
++ * Program the DQS Receiver Enable delay values for each lane
++ */
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+- /* 2.10.5.8.2 (3)
+- * Program DqsRcvTrEn = 1
+- */
+- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+- dword |= (0x1 << 13);
+- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+-
+- /* 2.10.5.8.2 (4)
+- * Issue 192 read requests to the target rank
+- */
+- generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
++ /* 2.10.5.8.2 (3)
++ * Program DqsRcvTrEn = 1
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword |= (0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++
++ /* 2.10.5.8.2 (4)
++ * Issue 192 read requests to the target rank
++ */
++ generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
++
++ /* 2.10.5.8.2 (5)
++ * Program DqsRcvTrEn = 0
++ */
++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
++ dword &= ~(0x1 << 13);
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+
+- /* 2.10.5.8.2 (5)
+- * Program DqsRcvTrEn = 0
+- */
+- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+- dword &= ~(0x1 << 13);
+- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
++ /* 2.10.5.8.2 (6)
++ * Read PhRecGrossDly, PhRecFineDly
++ */
++ read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+
+- /* 2.10.5.8.2 (6)
+- * Read PhRecGrossDly, PhRecFineDly
+- */
+- read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
++ /* 2.10.5.8.2 (7)
++ * Calculate and program the DQS Receiver Enable delay values
++ */
++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
++ current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
++ current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
++ if (nibble == 0) {
++ if (lane == 8)
++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
++ else
++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
++ } else {
++ /* 2.10.5.8.2 (1)
++ * Average the trained values of both nibbles on x4 DIMMs
++ */
++ if (lane == 8)
++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = (pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] + current_total_delay[lane]) / 2;
++ else
++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = (pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] + current_total_delay[lane]) / 2;
++ }
++ }
+
+- /* 2.10.5.8.2 (7)
+- * Calculate and program the DQS Receiver Enable delay values
+- */
+- for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+- current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
+- current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
+- if (lane == 8)
+- pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
+- else
+- pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
++#if DQS_TRAIN_DEBUG > 1
++ for (lane = 0; lane < 8; lane++)
++ printk(BIOS_DEBUG, "\t\tTrainRcvEn55: Channel: %d dimm: %d nibble: %d lane %d current_total_delay: %04x CH_D_B_RCVRDLY: %04x\n",
++ Channel, dimm, nibble, lane, current_total_delay[lane], pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane]);
++#endif
++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+ }
+- write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+ if (rank == 0) {
+ /* Back up the Rank 0 delays for later use */
+@@ -1396,7 +1419,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+
+ #if DQS_TRAIN_DEBUG > 0
+ for (lane = 0; lane < 8; lane++)
+- print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
++ print_debug_dqs_pair("\t\tTrainRcvEn56: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
+ #endif
+ }
+ }
+@@ -1816,15 +1839,23 @@ void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstatA)
++ struct DCTStatStruc *pDCTstatA, int16_t single_node_number)
+ {
+ u8 Node = 0;
+ struct DCTStatStruc *pDCTstat;
+
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
++ uint8_t start_node = 0;
++ uint8_t end_node = MAX_NODES_SUPPORTED;
++
++ if (single_node_number >= 0) {
++ start_node = single_node_number;
++ end_node = single_node_number;
++ }
++
+ /* FIXME: skip for Ax */
+- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ for (Node = start_node; Node < end_node; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ if (!pDCTstat->NodePresent)
+ continue;
+@@ -1848,6 +1879,8 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+ if (!pDCTstat->DIMMValidDCT[dct])
+ continue;
+
++ printk(BIOS_SPEW, "%s: training node %d DCT %d\n", __func__, Node, dct);
++
+ /* Back up D18F2x9C_x0000_0004_dct[1:0] */
+ datc_backup = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
+
+@@ -1986,6 +2019,8 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+
+ /* Restore D18F2x9C_x0000_0004_dct[1:0] */
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004, datc_backup);
++
++ printk(BIOS_SPEW, "%s: done training node %d DCT %d\n", __func__, Node, dct);
+ }
+ } else {
+ fenceDynTraining_D(pMCTstat, pDCTstat, 0);
+@@ -1998,7 +2033,7 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+ }
+
+ static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, u8 dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ u16 avRecValue;
+ u32 val;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+index 6b63ba0..3153e46 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+@@ -19,7 +19,7 @@
+ */
+
+ static void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat);
++ struct DCTStatStruc *pDCTstat, uint8_t dct);
+
+
+ static void AgesaDelay(u32 msec)
+@@ -353,11 +353,14 @@ static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
+ }
+
+ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat)
++ struct DCTStatStruc *pDCTstatA, uint8_t Node)
+ {
+ uint32_t dword;
+ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
++ struct DCTStatStruc *pDCTstat;
++ pDCTstat = pDCTstatA + Node;
++
+ if (is_fam15h()) {
+ /* Program F2x[1, 0]90[DisDllShutDownSR]=1. */
+ if (pDCTstat->DIMMValidDCT[0]) {
+@@ -391,7 +394,7 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ uint8_t dct;
+ for (dct = 0; dct < 2; dct++) {
+ if (pDCTstat->DIMMValidDCT[dct]) {
+- phyAssistedMemFnceTraining(pMCTstat, pDCTstat);
++ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA, Node);
+ InitPhyCompensation(pMCTstat, pDCTstat, dct);
+ }
+ }
+@@ -438,7 +441,12 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ else
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[1];
+
+- FreqChgCtrlWrd(pMCTstat, pDCTstat);
++ if (pDCTstat->DIMMValidDCT[0]) {
++ FreqChgCtrlWrd(pMCTstat, pDCTstat, 0);
++ }
++ if (pDCTstat->DIMMValidDCT[1]) {
++ FreqChgCtrlWrd(pMCTstat, pDCTstat, 1);
++ }
+ }
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index fb3b3d8..3391dcf 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -35,9 +35,9 @@ u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
+ void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ u8 dct, u8 dimm, BOOL wl);
+ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm);
+-void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass);
++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass, uint8_t nibble);
+ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass);
+-void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass);
++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble);
+
+ static int32_t abs(int32_t val) {
+ if (val < 0)
+@@ -76,6 +76,8 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ {
+ u8 ByteLane;
+ u32 Value, Addr;
++ uint8_t nibble = 0;
++ uint8_t train_both_nibbles;
+ u16 Addl_Data_Offset, Addl_Data_Port;
+ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+@@ -88,98 +90,108 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
+ TrDimmSelEnd, (u32)dimm);
+
+- if (is_fam15h()) {
+- /* Set TrNibbleSel = 0
+- *
+- * TODO: Add support for x4 DIMMs
+- */
+- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+- DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
+- 2, (u32)0);
+- }
++ train_both_nibbles = 0;
++ if (pDCTstat->Dimmx4Present)
++ if (is_fam15h())
++ train_both_nibbles = 1;
+
+- /* 2. Prepare the DIMMs for write levelization using DDR3-defined
+- * MR commands. */
+- prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
++ for (nibble = 0; nibble < (train_both_nibbles + 1); nibble++) {
++ printk(BIOS_SPEW, "AgesaHwWlPhase1: training nibble %d\n", nibble);
+
+- /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
+- * satisfy DDR3-defined internal DRAM timing.
+- */
+- if (is_fam15h())
+- precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
+- else
+- pMCTData->AgesaDelay(40);
++ if (is_fam15h()) {
++ /* Program F2x[1, 0]9C_x08[WrtLvTrEn]=0 */
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
++
++ /* Set TrNibbleSel */
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
++ 2, (uint32_t)nibble);
++ }
+
+- /* 4. Configure the processor's DDR phy for write levelization training: */
+- procConfig(pMCTstat, pDCTstat, dct, dimm, pass);
++ /* 2. Prepare the DIMMs for write levelization using DDR3-defined
++ * MR commands. */
++ prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
+
+- /* 5. Begin write levelization training:
+- * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
+- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
+- {
+- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+- DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
+- }
+- else
+- {
+- /* Broadcast write to all D3Dbyte chipset register offset 0xc
+- * Set bit 0 (wrTrain)
+- * Program bit 4 to nibble being trained (only matters for x4dimms)
+- * retain value of 3:2 (Trdimmsel)
+- * reset bit 5 (FrzPR)
++ /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
++ * satisfy DDR3-defined internal DRAM timing.
+ */
+- if (dct)
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
++ else
++ pMCTData->AgesaDelay(40);
++
++ /* 4. Configure the processor's DDR phy for write levelization training: */
++ procConfig(pMCTstat, pDCTstat, dct, dimm, pass, nibble);
++
++ /* 5. Begin write levelization training:
++ * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
+ {
+- Addl_Data_Offset=0x198;
+- Addl_Data_Port=0x19C;
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
+ }
+ else
+ {
+- Addl_Data_Offset=0x98;
+- Addl_Data_Port=0x9C;
++ /* Broadcast write to all D3Dbyte chipset register offset 0xc
++ * Set bit 0 (wrTrain)
++ * Program bit 4 to nibble being trained (only matters for x4dimms)
++ * retain value of 3:2 (Trdimmsel)
++ * reset bit 5 (FrzPR)
++ */
++ if (dct)
++ {
++ Addl_Data_Offset=0x198;
++ Addl_Data_Port=0x19C;
++ }
++ else
++ {
++ Addl_Data_Offset=0x98;
++ Addl_Data_Port=0x9C;
++ }
++ Addr=0x0D00000C;
++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
++ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
++ DctAccessDone, DctAccessDone)) == 0);
++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
++ Value = bitTestSet(Value, 0); /* enable WL training */
++ Value = bitTestReset(Value, 4); /* for x8 only */
++ Value = bitTestReset(Value, 5); /* for hardware WL training */
++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
++ Addr=0x4D030F0C;
++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
++ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
++ DctAccessDone, DctAccessDone)) == 0);
+ }
+- Addr=0x0D00000C;
+- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+- while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+- DctAccessDone, DctAccessDone)) == 0);
+- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+- Value = bitTestSet(Value, 0); /* enable WL training */
+- Value = bitTestReset(Value, 4); /* for x8 only */
+- Value = bitTestReset(Value, 5); /* for hardware WL training */
+- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+- Addr=0x4D030F0C;
+- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+- while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+- DctAccessDone, DctAccessDone)) == 0);
+- }
+
+- if (is_fam15h())
+- proc_MFENCE();
++ if (is_fam15h())
++ proc_MFENCE();
+
+- /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
+- if (is_fam15h())
+- precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
+- else
+- pMCTData->AgesaDelay(140);
++ /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
++ if (is_fam15h())
++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
++ else
++ pMCTData->AgesaDelay(140);
+
+- /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
+- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+- DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
++ /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
++ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
+
+- /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
+- * to get the gross and fine delay settings
+- * for the target DIMM and save these values. */
+- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+- getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass);
+- }
++ /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
++ * to get the gross and fine delay settings
++ * for the target DIMM and save these values. */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass, nibble);
++ }
+
+- pDCTData->WLCriticalGrossDelayPrevPass = 0x1f;
++ pDCTData->WLCriticalGrossDelayPrevPass = 0x0;
++ }
+
+ return 0;
+ }
+
+ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+- u8 dct, u8 dimm, u8 pass)
++ uint8_t dct, uint8_t dimm, uint8_t pass)
+ {
+ u8 ByteLane;
+ uint8_t status = 0;
+@@ -190,6 +202,12 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
+ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+
++ printk(BIOS_SPEW, "\toriginal critical gross delay: %d\n", cgd);
++
++ /* FIXME
++ * For now, disable CGD adjustment as it seems to interfere with registered DIMM training
++ */
++
+ /* Calculate the Critical Gross Delay */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ /* Calculate the gross delay differential for this lane */
+@@ -205,6 +223,8 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ cgd = gross_diff[ByteLane];
+ }
+
++ printk(BIOS_SPEW, "\tnew critical gross delay: %d\n", cgd);
++
+ pDCTData->WLCriticalGrossDelayPrevPass = cgd;
+
+ if (pDCTstat->Speed != pDCTstat->TargetFreq) {
+@@ -281,7 +301,7 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
+ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
+
+- /* Prevent underflow in the presence of noise / instability*/
++ /* Prevent underflow in the presence of noise / instability */
+ if (gross_diff[ByteLane] < cgd)
+ gross_diff[ByteLane] = cgd;
+
+@@ -289,7 +309,8 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
+ }
+ } else {
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
+- dword &= ~(0x3 << 24); /* WrDqDqsEarly = 0 */
++ dword &= ~(0x3 << 24); /* WrDqDqsEarly = pDCTData->WrDqsGrossDlyBaseOffset */
++ dword |= ((pDCTData->WrDqsGrossDlyBaseOffset & 0x3) << 24);
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
+ }
+ }
+@@ -959,7 +980,7 @@ static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
+ #endif
+
+ /*-----------------------------------------------------------------------------
+- * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
++ * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass, u8 Nibble)
+ *
+ * Description:
+ * This function programs the ODT values for the NB
+@@ -972,13 +993,14 @@ static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+-void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass)
++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass, uint8_t nibble)
+ {
+ u8 ByteLane, MemClkFreq;
+ int32_t Seed_Gross;
+ int32_t Seed_Fine;
+ uint8_t Seed_PreGross;
+ u32 Value, Addr;
++ uint32_t dword;
+ u16 Addl_Data_Offset, Addl_Data_Port;
+ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+@@ -1048,10 +1070,17 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+ uint16_t Seed_Total = 0;
++ pDCTData->WrDqsGrossDlyBaseOffset = 0x0;
+ if (package_type == PT_GR) {
+ /* Socket G34: Fam15h BKDG v3.14 Table 96 */
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ /* TODO
++ * Implement mainboard-specific seed and
++ * WrDqsGrossDly base overrides.
++ * 0x41 and 0x0 are the "stock" values
++ */
+ Seed_Total = 0x41;
++ pDCTData->WrDqsGrossDlyBaseOffset = 0x2;
+ } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
+ Seed_Total = 0x0;
+ } else {
+@@ -1133,15 +1162,16 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ printk(BIOS_SPEW, "\tLane %02x initial seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+ }
+ } else {
+- /* Pass 2 */
+- /* From BKDG, Write Leveling Seed Value. */
+- if (is_fam15h()) {
+- uint32_t RegisterDelay;
+- int32_t SeedTotal;
+- int32_t SeedTotalPreScaling;
+- uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++ if (nibble == 0) {
++ /* Pass 2 */
++ /* From BKDG, Write Leveling Seed Value. */
++ if (is_fam15h()) {
++ uint32_t RegisterDelay;
++ int32_t SeedTotal[MAX_BYTE_LANES];
++ int32_t SeedTotalPreScaling[MAX_BYTE_LANES];
++ uint32_t WrDqDqsEarly;
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+
+- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ if (AddrCmdPrelaunch)
+ RegisterDelay = 0x30;
+@@ -1150,84 +1180,133 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ } else {
+ RegisterDelay = 0;
+ }
++
+ /* Retrieve WrDqDqsEarly */
+- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value);
++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
++ WrDqDqsEarly = (dword >> 24) & 0x3;
+
+- /* Calculate adjusted seed values */
+- SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+- ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
+- SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value));
+- SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) *
+- fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
++ /* FIXME
++ * Ignore WrDqDqsEarly for now to work around training issues
++ */
++ WrDqDqsEarly = 0;
+
+- if (SeedTotal >= 0) {
+- Seed_Gross = SeedTotal / 32;
+- Seed_Fine = SeedTotal % 32;
+- } else {
+- Seed_Gross = (SeedTotal / 32) - 1;
+- Seed_Fine = (SeedTotal % 32) + 32;
++ /* Generate new seed values */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ /* Calculate adjusted seed values */
++ SeedTotal[ByteLane] = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
++ SeedTotalPreScaling[ByteLane] = (SeedTotal[ByteLane] - RegisterDelay - (0x20 * WrDqDqsEarly));
++ SeedTotal[ByteLane] = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling[ByteLane]) *
++ fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
+ }
+
+- if (Seed_Gross == 0)
+- Seed_PreGross = 0;
+- else if (Seed_Gross & 0x1)
+- Seed_PreGross = 1;
+- else
+- Seed_PreGross = 2;
++ /* Generate register values from seeds */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ printk(BIOS_SPEW, "\tLane %02x scaled delay: %04x\n", ByteLane, SeedTotal[ByteLane]);
+
+- /* Save seed values for later use */
+- pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+- pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+- pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++ if (SeedTotal[ByteLane] >= 0) {
++ Seed_Gross = SeedTotal[ByteLane] / 32;
++ Seed_Fine = SeedTotal[ByteLane] % 32;
++ } else {
++ Seed_Gross = (SeedTotal[ByteLane] / 32) - 1;
++ Seed_Fine = (SeedTotal[ByteLane] % 32) + 32;
++ }
+
+- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ if (Seed_Gross == 0)
++ Seed_PreGross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_PreGross = 1;
++ else
++ Seed_PreGross = 2;
+
+- printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+- }
+- } else {
+- uint32_t RegisterDelay;
+- uint32_t SeedTotalPreScaling;
+- uint32_t SeedTotal;
+- uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+- {
+- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+- if (AddrCmdPrelaunch == 0)
+- RegisterDelay = 0x20;
++ /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
++ * due to the long register delays causing premature total delay wrap-around.
++ * Attempt to work around this...
++ */
++ Seed_PreGross = Seed_Gross;
++
++ /* Save seed values for later use */
++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
++ }
++ } else {
++ uint32_t RegisterDelay;
++ uint32_t SeedTotalPreScaling;
++ uint32_t SeedTotal;
++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
++ {
++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
++ if (AddrCmdPrelaunch == 0)
++ RegisterDelay = 0x20;
++ else
++ RegisterDelay = 0x30;
++ } else {
++ RegisterDelay = 0;
++ }
++ SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
++ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5)) - RegisterDelay;
++ /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
++ training) - RegisterDelay. */
++ SeedTotal = (uint16_t) ((((uint64_t) SeedTotalPreScaling) *
++ fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100));
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal & 0x1f;
++ if (Seed_Gross == 0)
++ Seed_Gross = 0;
++ else if (Seed_Gross & 0x1)
++ Seed_Gross = 1;
+ else
+- RegisterDelay = 0x30;
+- } else {
+- RegisterDelay = 0;
++ Seed_Gross = 2;
++
++ /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
++ * due to the long register delays causing premature total delay wrap-around.
++ * Attempt to work around this...
++ */
++ SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f);
++ SeedTotal += RegisterDelay;
++ Seed_Gross = SeedTotal / 32;
++ Seed_Fine = SeedTotal & 0x1f;
++
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
+ }
+- SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+- (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5)) - RegisterDelay;
+- /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
+- training) - RegisterDelay. */
+- SeedTotal = (uint16_t) ((((uint64_t) SeedTotalPreScaling) *
+- fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100));
+- Seed_Gross = SeedTotal / 32;
+- Seed_Fine = SeedTotal & 0x1f;
+- if (Seed_Gross == 0)
+- Seed_Gross = 0;
+- else if (Seed_Gross & 0x1)
+- Seed_Gross = 1;
+- else
+- Seed_Gross = 2;
+-
+- /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
+- * due to the long register delays causing premature total delay wrap-around.
+- * Attempt to work around this...
+- */
+- SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f);
+- SeedTotal += RegisterDelay;
+- Seed_Gross = SeedTotal / 32;
+- Seed_Fine = SeedTotal & 0x1f;
++ }
+
+- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
++ /* Save initial seeds for upper nibble pass */
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane];
++ }
++ } else {
++ /* Restore seed values from lower nibble pass */
++ if (is_fam15h()) {
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
++ }
++ } else {
++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
+
+- printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
++ printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
++ }
+ }
+ }
+ }
+@@ -1358,7 +1437,7 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
+ }
+
+ /*-----------------------------------------------------------------------------
+- * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm)
++ * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm, u8 Nibble)
+ *
+ * Description:
+ * This function reads the write levelization byte delay from the Phase
+@@ -1376,7 +1455,7 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
+ *
+ *-----------------------------------------------------------------------------
+ */
+-void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass)
++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble)
+ {
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+ u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
+@@ -1427,7 +1506,16 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
+ fine = 0;
+ }
+ }
+- pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
+- pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
+- printk(BIOS_SPEW, "\tLane %02x final adjusted value: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f));
++ if (nibble == 0) {
++ pDCTData->WLFineDelay[index+ByteLane] = (uint8_t)fine;
++ pDCTData->WLGrossDelay[index+ByteLane] = (uint8_t)gross;
++ } else {
++ uint32_t WLTotalDelay = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
++ WLTotalDelay += ((gross & 0x1f) << 5) | (fine & 0x1f);
++ WLTotalDelay /= 2;
++ pDCTData->WLFineDelay[index+ByteLane] = (uint8_t)(WLTotalDelay & 0x1f);
++ pDCTData->WLGrossDelay[index+ByteLane] = (uint8_t)((WLTotalDelay >> 5) & 0x1f);
++ }
++
++ printk(BIOS_SPEW, "\tLane %02x adjusted value: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f));
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+index 12e7c4a..3337c14 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+@@ -119,16 +119,21 @@ typedef struct _sDCTStruct
+ u8 DctTrain; /* Current DCT being trained */
+ u8 CurrDct; /* Current DCT number (0 or 1) */
+ u8 DctCSPresent; /* Current DCT CS mapping */
++ uint8_t WrDqsGrossDlyBaseOffset;
+ int32_t WLSeedGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Gross Delay */
+ /* per byte Lane Per Logical DIMM*/
+ int32_t WLSeedFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Fine Delay */
+ /* per byte Lane Per Logical DIMM*/
+ int32_t WLSeedPreGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Pre-Gross Delay */
+ /* per byte Lane Per Logical DIMM*/
+- u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */
+- /* per byte Lane Per Logical DIMM*/
+- u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */
+- /* per byte Lane Per Logical DIMM*/
++ uint8_t WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
++ uint8_t WLSeedGrossPrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
++ uint8_t WLSeedFinePrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */
++ /* per byte Lane Per Logical DIMM*/
++ u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */
++ /* per byte Lane Per Logical DIMM*/
+ u8 WLGrossDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Gross Delay */
+ /* per byte Lane Per Logical DIMM*/
+ u8 WLFineDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Fine Delay */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch b/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch
new file mode 100644
index 00000000..22353241
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch
@@ -0,0 +1,948 @@
+From 37de124d5fb295bd2d39201a9c3a0a0805a38327 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 30 Jul 2015 14:07:15 -0500
+Subject: [PATCH 089/146] cpu/amd/model_10xxx: Fix Family 15h multiple package
+ support
+
+TEST: Booted ASUS KGPE-D16 with two Opteron 6328 processors
+and several different RDIMM configurations.
+---
+ src/cpu/amd/car/cache_as_ram.inc | 17 +++-
+ src/cpu/amd/model_10xxx/defaults.h | 101 ++++++++++++++++---
+ src/cpu/amd/model_10xxx/fidvid.c | 81 +++++++--------
+ src/cpu/amd/model_10xxx/init_cpus.c | 66 +++++++++++--
+ src/cpu/amd/quadcore/quadcore.c | 19 +---
+ src/cpu/amd/quadcore/quadcore_id.c | 1 -
+ src/mainboard/asus/kgpe-d16/romstage.c | 46 +++++++--
+ src/northbridge/amd/amdht/h3finit.c | 57 ++++++++++-
+ src/northbridge/amd/amdht/h3ncmn.c | 30 +++++-
+ src/northbridge/amd/amdht/ht_wrapper.c | 141 +++++++++++++++++++++++++--
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 1 +
+ 11 files changed, 458 insertions(+), 102 deletions(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 9a51e3c..7d71171 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -525,8 +525,23 @@ CAR_FAM10_ap:
+ /* Fam10h NB config bit 54 was not set */
+ rolb %cl, %bl
+ roll_cfg:
++ jmp_if_not_fam15h(ap_apicid_ready)
++ cmp $0x5, %ecx
++ jne ap_apicid_ready
+
+- /* Calculate stack pointer. */
++ /* This is a multi-node CPU
++ * Adjust the maximum APIC ID to a more reasonable value
++ * given that no 32-core Family 15h processors exist
++ */
++ movl %ebx, %ecx
++ and $0x0f, %ecx /* Get lower 4 bits of CPU number */
++ and $0x60, %ebx /* Get node ID */
++ shrl $0x1, %ebx /* Shift node ID part of APIC ID down by 1 */
++ or %ecx, %ebx /* Recombine node ID and CPU number */
++
++ap_apicid_ready:
++
++ /* Calculate stack pointer using adjusted APIC ID stored in ebx */
+ movl $CacheSizeAPStack, %eax
+ mull %ebx
+ movl $(CacheBase + (CacheSize - (CacheSizeBSPStack + CacheSizeBSPSlush))), %esp
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 24f87ba..513d169 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -244,18 +244,50 @@ static const struct {
+ { 0, 0x68, (AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00200000, 0x00600000 }, /* [22:21] DsNpReqLmt0 = 01b */
+
+- { 0, 0x84, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xC4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xC4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xE4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xE4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
++ /* FIXME
++ * Non-C32 packages only
++ */
++ { 0, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xA4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xC4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xE4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ /* FIXME
++ * C32 package only
++ */
++#if 0
++ { 0, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xA4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xC4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xE4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++#endif
++
+ /* Link Global Retry Control Register */
+ { 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00073900, 0x00073F00 },
+@@ -614,38 +646,79 @@ static const struct {
+ { 0x530A, AMD_DR_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x00004400, 0x00006400 }, /* HT_PHY_DLL_REG */
+
+- { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
++ completeness */
++
++ { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
++ completeness */
++
++ { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++
++ { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++
++ /* Link Phy Receiver Loop Filter Registers */
++ { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
++ [21:14] LfcMin = 10h */
++
++ { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
++ [21:14] LfcMin = 10h */
++
++ { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
++ [21:14] LfcMin = 08h */
++
++ { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
++ [21:14] LfcMin = 08h */
++
++ { 0xC0, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
++ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
++ [20:16] RttIndex = 04h */
++
++/* FIXME
++ * Causes lockups for some reason when more than one package is installed
++ * Debug and reactivate!
++ */
++// #if 0
++ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xDF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+- { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xDF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+ /* Link Phy Receiver Loop Filter Registers */
+- { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xD1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xC1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xD1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xC1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC0, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
++ { 0xC0, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+- [20:16] RttIndex = 04h */
++ [20:16] RttIndex = 04h */
++// #endif
+ };
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 0e870e3..fe1cfb4 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -633,44 +633,45 @@ static void prep_fid_change(void)
+ }
+
+ static void waitCurrentPstate(u32 target_pstate) {
+- msr_t initial_msr = rdmsr(TSC_MSR);
+- msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
+- msr_t tsc_msr;
+- u8 timedout ;
+-
+- /* paranoia ? I fear when we run fixPsNbVidBeforeWR we can enter a
+- * P1 that is a copy of P0, therefore has the same NB DID but the
+- * TSC will count twice per tick, so we have to wait for twice the
+- * count to achieve the desired timeout. But I'm likely to
+- * misunderstand this...
+- */
+- u32 corrected_timeout = ( (pstate_msr.lo==1)
+- && (!(rdmsr(0xC0010065).lo & NB_DID_M_ON)) ) ?
+- WAIT_PSTATE_TIMEOUT*2 : WAIT_PSTATE_TIMEOUT ;
+- msr_t timeout;
+-
+- timeout.lo = initial_msr.lo + corrected_timeout ;
+- timeout.hi = initial_msr.hi;
+- if ( (((u32)0xffffffff) - initial_msr.lo) < corrected_timeout ) {
+- timeout.hi++;
+- }
+-
+- // assuming TSC ticks at 1.25 ns per tick (800 MHz)
+- do {
+- pstate_msr = rdmsr(CUR_PSTATE_MSR);
+- tsc_msr = rdmsr(TSC_MSR);
+- timedout = (tsc_msr.hi > timeout.hi)
+- || ((tsc_msr.hi == timeout.hi) && (tsc_msr.lo > timeout.lo ));
+- } while ( (pstate_msr.lo != target_pstate) && (! timedout) ) ;
+-
+- if (pstate_msr.lo != target_pstate) {
+- msr_t limit_msr = rdmsr(0xc0010061);
+- printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n", target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo);
+-
+- do { // should we just go on instead ?
+- pstate_msr = rdmsr(CUR_PSTATE_MSR);
+- } while ( pstate_msr.lo != target_pstate ) ;
+- }
++ msr_t initial_msr = rdmsr(TSC_MSR);
++ msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ msr_t tsc_msr;
++ u8 timedout ;
++
++ /* paranoia ? I fear when we run fixPsNbVidBeforeWR we can enter a
++ * P1 that is a copy of P0, therefore has the same NB DID but the
++ * TSC will count twice per tick, so we have to wait for twice the
++ * count to achieve the desired timeout. But I'm likely to
++ * misunderstand this...
++ */
++ u32 corrected_timeout = ((pstate_msr.lo==1)
++ && (!(rdmsr(0xC0010065).lo & NB_DID_M_ON)) ) ?
++ WAIT_PSTATE_TIMEOUT*2 : WAIT_PSTATE_TIMEOUT;
++ msr_t timeout;
++
++ timeout.lo = initial_msr.lo + corrected_timeout ;
++ timeout.hi = initial_msr.hi;
++ if ( (((u32)0xffffffff) - initial_msr.lo) < corrected_timeout ) {
++ timeout.hi++;
++ }
++
++ // assuming TSC ticks at 1.25 ns per tick (800 MHz)
++ do {
++ pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ tsc_msr = rdmsr(TSC_MSR);
++ timedout = (tsc_msr.hi > timeout.hi)
++ || ((tsc_msr.hi == timeout.hi) && (tsc_msr.lo > timeout.lo ));
++ } while ( (pstate_msr.lo != target_pstate) && (! timedout) ) ;
++
++ if (pstate_msr.lo != target_pstate) {
++ msr_t limit_msr = rdmsr(0xc0010061);
++ printk(BIOS_ERR, "*** APIC ID %02x: timed out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n",
++ cpuid_ebx(0x00000001) >> 24, target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo);
++
++ do { // should we just go on instead ?
++ pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ } while ( pstate_msr.lo != target_pstate ) ;
++ }
+ }
+
+ static void set_pstate(u32 nonBoostedPState) {
+@@ -1063,13 +1064,13 @@ static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes)
+ APs and BSP */
+ ap_apicidx.num = 0;
+
+- for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE_RANGE, store_ap_apicid, &ap_apicidx);
++ for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE_RANGE, -1, store_ap_apicid, &ap_apicidx);
+
+ for (i = 0; i < ap_apicidx.num; i++) {
+ init_fidvid_bsp_stage1(ap_apicidx.apicid[i], &fv);
+ }
+ #else
+- for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE0_ONLY, init_fidvid_bsp_stage1, &fv);
++ for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE0_ONLY, -1, init_fidvid_bsp_stage1, &fv);
+ #endif
+
+ print_debug_fv("common_fid = ", fv.common_fid);
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 0d1f043..e7f146f 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -59,6 +59,8 @@ static void set_EnableCf8ExtCfg(void)
+ static void set_EnableCf8ExtCfg(void) { }
+ #endif
+
++// #define DEBUG_HT_SETUP 1
++// #define FAM10_AP_NODE_SEQUENTIAL_START 1
+
+ typedef void (*process_ap_t) (u32 apicid, void *gp);
+
+@@ -143,8 +145,8 @@ uint32_t get_boot_apic_id(uint8_t node, uint32_t core) {
+ //core range = 1 : core 0 only
+ //core range = 2 : cores other than core0
+
+-static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+- void *gp)
++static void for_each_ap(uint32_t bsp_apicid, uint32_t core_range, int8_t node,
++ process_ap_t process_ap, void *gp)
+ {
+ // here assume the OS don't change our apicid
+ u32 ap_apicid;
+@@ -165,6 +167,9 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ }
+
+ for (i = 0; i < nodes; i++) {
++ if ((node >= 0) && (i != node))
++ continue;
++
+ cores_found = get_core_num_in_bsp(i);
+
+ u32 jstart, jend;
+@@ -280,7 +285,7 @@ void wait_all_other_cores_started(u32 bsp_apicid)
+ {
+ // all aps other than core0
+ printk(BIOS_DEBUG, "started ap apicid: ");
+- for_each_ap(bsp_apicid, 2, wait_ap_started, (void *)0);
++ for_each_ap(bsp_apicid, 2, -1, wait_ap_started, (void *)0);
+ printk(BIOS_DEBUG, "\n");
+ }
+
+@@ -373,8 +378,10 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ /* NB_CFG MSR is shared between cores, so we need make sure
+ core0 is done at first --- use wait_all_core0_started */
+ if (id.coreid == 0) {
+- set_apicid_cpuid_lo(); /* only set it on core0 */
+- set_EnableCf8ExtCfg(); /* only set it on core0 */
++ /* Set InitApicIdCpuIdLo / EnableCf8ExtCfg on core0 only */
++ if (!is_fam15h())
++ set_apicid_cpuid_lo();
++ set_EnableCf8ExtCfg();
+ #if CONFIG_ENABLE_APIC_EXT_ID
+ enable_apic_ext_id(id.nodeid);
+ #endif
+@@ -427,6 +434,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ }
+ // Mark the core as started.
+ lapic_write(LAPIC_MSG_REG, (apicid << 24) | F10_APSTATE_STARTED);
++ printk(BIOS_DEBUG, "CPU APICID %02x start flag set\n", apicid);
+
+ if (apicid != bsp_apicid) {
+ /* Setup each AP's cores MSRs.
+@@ -588,6 +596,34 @@ static void setup_remote_node(u8 node)
+ }
+ #endif /* CONFIG_MAX_PHYSICAL_CPUS > 1 */
+
++//it is running on core0 of node0
++static void start_other_cores(uint32_t bsp_apicid)
++{
++ u32 nodes;
++ u32 nodeid;
++
++ // disable multi_core
++ if (read_option(multi_core, 0) != 0) {
++ printk(BIOS_DEBUG, "Skip additional core init\n");
++ return;
++ }
++
++ nodes = get_nodes();
++
++ for (nodeid = 0; nodeid < nodes; nodeid++) {
++ u32 cores = get_core_num_in_bsp(nodeid);
++ printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1\n", nodeid, cores);
++ if (cores > 0) {
++ real_start_other_core(nodeid, cores);
++#ifdef FAM10_AP_NODE_SEQUENTIAL_START
++ printk(BIOS_DEBUG, "waiting for core start on node %d...\n", nodeid);
++ for_each_ap(bsp_apicid, 2, nodeid, wait_ap_started, (void *)0);
++ printk(BIOS_DEBUG, "...started\n");
++#endif
++ }
++ }
++}
++
+ static void AMD_Errata281(u8 node, uint64_t revision, u32 platform)
+ {
+ /* Workaround for Transaction Scheduling Conflict in
+@@ -847,6 +883,10 @@ static void AMD_SetHtPhyRegister(u8 node, u8 link, u8 entry)
+
+ phyBase = ((u32) link << 3) | 0x180;
+
++ /* Determine if link is connected and abort if not */
++ if (!(pci_read_config32(NODE_PCI(node, 0), 0x98 + (link * 0x20)) & 0x1))
++ return;
++
+ /* Get the portal control register's initial value
+ * and update it to access the desired phy register
+ */
+@@ -1005,10 +1045,11 @@ static void cpuSetAMDPCI(u8 node)
+ * Hypertransport initialization has taken place. Also note
+ * that it is run for the first core on each node
+ */
+- u8 i, j;
++ uint8_t i;
++ uint8_t j;
+ u32 platform;
+ u32 val;
+- u8 offset;
++ uint8_t offset;
+ uint32_t dword;
+ uint64_t revision;
+
+@@ -1035,6 +1076,17 @@ static void cpuSetAMDPCI(u8 node)
+ }
+ }
+
++#ifdef DEBUG_HT_SETUP
++ /* Dump link settings */
++ for (i = 0; i < 4; i++) {
++ for (j = 0; j < 4; j++) {
++ printk(BIOS_DEBUG, "Node %d link %d: type register: %08x control register: %08x extended control sublink 0: %08x 1: %08x\n", i, j,
++ pci_read_config32(NODE_PCI(i, 0), 0x98 + (j * 0x20)), pci_read_config32(NODE_PCI(i, 0), 0x84 + (j * 0x20)),
++ pci_read_config32(NODE_PCI(i, 0), 0x170 + (j * 0x4)), pci_read_config32(NODE_PCI(i, 0), 0x180 + (j * 0x4)));
++ }
++ }
++#endif
++
+ for (i = 0; i < ARRAY_SIZE(fam10_htphy_default); i++) {
+ if ((fam10_htphy_default[i].revision & revision) &&
+ (fam10_htphy_default[i].platform & platform)) {
+diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c
+index 8a9b5ed..9c31eac 100644
+--- a/src/cpu/amd/quadcore/quadcore.c
++++ b/src/cpu/amd/quadcore/quadcore.c
+@@ -31,21 +31,6 @@
+ uint32_t get_boot_apic_id(uint8_t node, uint32_t core);
+ uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2);
+
+-static inline uint8_t is_fam15h(void)
+-{
+- uint8_t fam15h = 0;
+- uint32_t family;
+-
+- family = cpuid_eax(0x80000001);
+- family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+-
+- if (family >= 0x6f)
+- /* Family 15h or later */
+- fam15h = 1;
+-
+- return fam15h;
+-}
+-
+ static u32 get_core_num_in_bsp(u32 nodeid)
+ {
+ u32 dword;
+@@ -141,6 +126,7 @@ static void real_start_other_core(uint32_t nodeid, uint32_t cores)
+ }
+ }
+
++#if (!IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX))
+ //it is running on core0 of node0
+ static void start_other_cores(void)
+ {
+@@ -157,9 +143,10 @@ static void start_other_cores(void)
+
+ for (nodeid = 0; nodeid < nodes; nodeid++) {
+ u32 cores = get_core_num_in_bsp(nodeid);
+- printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1 \n", nodeid, cores);
++ printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1\n", nodeid, cores);
+ if (cores > 0) {
+ real_start_other_core(nodeid, cores);
+ }
+ }
+ }
++#endif
+diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c
+index c0537b3..1f5cbd8 100644
+--- a/src/cpu/amd/quadcore/quadcore_id.c
++++ b/src/cpu/amd/quadcore/quadcore_id.c
+@@ -108,7 +108,6 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ id.nodeid = apicid & 0x7;
+ }
+ }
+-
+ if (fam15h && dual_node) {
+ /* Coreboot expects each separate processor die to be on a different nodeid.
+ * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 0df2447..acc8308 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -97,7 +97,18 @@ static void switch_spd_mux(uint8_t channel)
+ pci_write_config8(PCI_DEV(0, 0x14, 0), 0x54, byte);
+ }
+
+-static const uint8_t spd_addr[] = {
++static const uint8_t spd_addr_fam15[] = {
++ // Socket 0 Node 0 ("Node 0")
++ RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++ // Socket 0 Node 1 ("Node 1")
++ RC00, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++ // Socket 1 Node 0 ("Node 2")
++ RC01, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++ // Socket 1 Node 1 ("Node 3")
++ RC01, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++};
++
++static const uint8_t spd_addr_fam10[] = {
+ // Socket 0 Node 0 ("Node 0")
+ RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
+ // Socket 0 Node 1 ("Node 1")
+@@ -117,10 +128,10 @@ static void activate_spd_rom(const struct mem_controller *ctrl) {
+ switch_spd_mux(0x2);
+ } else if (ctrl->node_id == 1) {
+ printk(BIOS_DEBUG, "enable_spd_node1()\n");
+- switch_spd_mux((sysinfo->nodes <= 2)?0x2:0x3);
++ switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x2:0x3);
+ } else if (ctrl->node_id == 2) {
+ printk(BIOS_DEBUG, "enable_spd_node2()\n");
+- switch_spd_mux((sysinfo->nodes <= 2)?0x3:0x2);
++ switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x3:0x2);
+ } else if (ctrl->node_id == 3) {
+ printk(BIOS_DEBUG, "enable_spd_node3()\n");
+ switch_spd_mux(0x3);
+@@ -306,18 +317,25 @@ void initialize_romstage_console_lock(void)
+
+ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
++ uint32_t esp;
++ __asm__ volatile (
++ "movl %%esp, %0"
++ : "=r" (esp)
++ );
++
+ struct sys_info *sysinfo = &sysinfo_car;
+
+ uint32_t bsp_apicid = 0, val;
+ uint8_t byte;
+ msr_t msr;
+
+- timestamp_init(timestamp_get());
+- timestamp_add_now(TS_START_ROMSTAGE);
+-
+ int s3resume = acpi_is_wakeup_s3();
+
+ if (!cpu_init_detectedx && boot_cpu()) {
++ /* Initial timestamp */
++ timestamp_init(timestamp_get());
++ timestamp_add_now(TS_START_ROMSTAGE);
++
+ /* Initialize the printk spinlock */
+ initialize_romstage_console_lock();
+
+@@ -344,6 +362,8 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ pci_write_config8(PCI_DEV(0, 0x14, 3), 0x78, byte);
+ }
+
++ printk(BIOS_SPEW, "Initial stack pointer: %08x\n", esp);
++
+ post_code(0x30);
+
+ if (bist == 0)
+@@ -397,7 +417,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
+ /* Core0 on each node is configured. Now setup any additional cores. */
+ printk(BIOS_DEBUG, "start_other_cores()\n");
+- start_other_cores();
++ start_other_cores(bsp_apicid);
+ post_code(0x37);
+ wait_all_other_cores_started(bsp_apicid);
+ }
+@@ -455,7 +475,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+
+ /* It's the time to set ctrl in sysinfo now; */
+ printk(BIOS_DEBUG, "fill_mem_ctrl() detected %d nodes\n", sysinfo->nodes);
+- fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr);
++ if (is_fam15h())
++ fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam15);
++ else
++ fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam10);
+ post_code(0x3D);
+
+ #if 0
+@@ -527,5 +550,12 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ */
+ BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
+ {
++ /* Force BUID to 0 */
++ static const u8 swaplist[] = {0, 0, 0xFF, 0, 0xFF};
++ if ((node == 0) && (link == 1)) { /* BSP SB link */
++ *List = swaplist;
++ return 1;
++ }
++
+ return 0;
+ }
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdht/h3finit.c b/src/northbridge/amd/amdht/h3finit.c
+index 849f4a8..82bf885 100644
+--- a/src/northbridge/amd/amdht/h3finit.c
++++ b/src/northbridge/amd/amdht/h3finit.c
+@@ -389,13 +389,49 @@ static u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
+ */
+ static void htDiscoveryFloodFill(sMainData *pDat)
+ {
+- u8 currentNode = 0;
+- u8 currentLink;
++ uint8_t currentNode = 0;
++ uint8_t currentLink;
++ uint8_t currentLinkID;
++
++ /* NOTE
++ * Each node inside a dual node (socket G34) processor must share
++ * an adjacent node ID. Alter the link scan order such that the
++ * other internal node is always scanned first...
++ */
++ uint8_t currentLinkScanOrder_Default[8] = {0, 1, 2, 3, 4, 5, 6, 7};
++ uint8_t currentLinkScanOrder_G34_Fam10[8] = {1, 0, 2, 3, 4, 5, 6, 7};
++ uint8_t currentLinkScanOrder_G34_Fam15[8] = {2, 0, 1, 3, 4, 5, 6, 7};
++
++ uint8_t fam15h = 0;
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
++
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f) {
++ /* Family 15h or later */
++ fam15h = 1;
++ }
++
++ if ((model >= 0x8) || fam15h)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
+
+ /* Entries are always added in pairs, the even indices are the 'source'
+ * side closest to the BSP, the odd indices are the 'destination' side
+ */
+-
+ while (currentNode <= pDat->NodesDiscovered)
+ {
+ u32 temp;
+@@ -423,11 +459,24 @@ static void htDiscoveryFloodFill(sMainData *pDat)
+ /* Enable routing tables on currentNode*/
+ pDat->nb->enableRoutingTables(currentNode, pDat->nb);
+
+- for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
++ for (currentLinkID = 0; currentLinkID < pDat->nb->maxLinks; currentLinkID++)
+ {
+ BOOL linkfound;
+ u8 token;
+
++ if (currentLinkID < 8) {
++ if (dual_node) {
++ if (fam15h)
++ currentLink = currentLinkScanOrder_G34_Fam15[currentLinkID];
++ else
++ currentLink = currentLinkScanOrder_G34_Fam10[currentLinkID];
++ } else {
++ currentLink = currentLinkScanOrder_Default[currentLinkID];
++ }
++ } else {
++ currentLink = currentLinkID;
++ }
++
+ if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
+ continue;
+
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index 8f9177f..1026d0e 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -51,8 +51,9 @@
+ #define REG_NODE_ID_0X60 0x60
+ #define REG_UNIT_ID_0X64 0x64
+ #define REG_LINK_TRANS_CONTROL_0X68 0x68
+-#define REG_LINK_INIT_CONTROL_0X6C 0x6C
++#define REG_LINK_INIT_CONTROL_0X6C 0x6c
+ #define REG_HT_CAP_BASE_0X80 0x80
++#define REG_NORTHBRIDGE_CFG_3X8C 0x8c
+ #define REG_HT_LINK_RETRY0_0X130 0x130
+ #define REG_HT_TRAFFIC_DIST_0X164 0x164
+ #define REG_HT_LINK_EXT_CONTROL0_0X170 0x170
+@@ -91,6 +92,21 @@
+ *** FAMILY/NORTHBRIDGE SPECIFIC FUNCTIONS ***
+ ***************************************************************************/
+
++static inline uint8_t is_fam15h(void)
++{
++ uint8_t fam15h = 0;
++ uint32_t family;
++
++ family = cpuid_eax(0x80000001);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ return fam15h;
++}
++
+ /***************************************************************************//**
+ *
+ * SBDFO
+@@ -219,8 +235,18 @@ static void writeRoutingTable(u8 node, u8 target, u8 link, cNorthBridge *nb)
+
+ static void writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb)
+ {
+- u32 temp = nodeID;
++ u32 temp;
+ ASSERT((node < nb->maxNodes) && (nodeID < nb->maxNodes));
++ if (is_fam15h()) {
++ temp = 1;
++ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_03,
++ REG_NORTHBRIDGE_CFG_3X8C),
++ 22, 22, &temp);
++ }
++ temp = nodeID;
+ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
+ makePCIBusFromNode(node),
+ makePCIDeviceFromNode(node),
+diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c
+index 0c6b474..772db1c 100644
+--- a/src/northbridge/amd/amdht/ht_wrapper.c
++++ b/src/northbridge/amd/amdht/ht_wrapper.c
+@@ -90,16 +90,132 @@ static u32 get_nodes(void)
+ */
+ static void AMD_CB_EventNotify (u8 evtClass, u16 event, const u8 *pEventData0)
+ {
+- u8 i;
++ uint8_t i;
++ uint8_t log_level;
++ uint8_t dump_event_detail;
+
+- printk(BIOS_DEBUG, "AMD_CB_EventNotify()\n");
+- printk(BIOS_DEBUG, " event class: %02x\n event: %04x\n data: ", evtClass, event);
++ printk(BIOS_DEBUG, "AMD_CB_EventNotify(): ");
+
+- for (i = 0; i < *pEventData0; i++) {
+- printk(BIOS_DEBUG, " %02x ", *(pEventData0 + i));
++ /* Decode event */
++ dump_event_detail = 1;
++ switch (evtClass) {
++ case HT_EVENT_CLASS_CRITICAL:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "CRITICAL");
++ break;
++ case HT_EVENT_CLASS_ERROR:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "ERROR");
++ break;
++ case HT_EVENT_CLASS_HW_FAULT:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "HARDWARE FAULT");
++ break;
++ case HT_EVENT_CLASS_WARNING:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "WARNING");
++ break;
++ case HT_EVENT_CLASS_INFO:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "INFO");
++ break;
++ default:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "UNKNOWN");
++ break;
+ }
+- printk(BIOS_DEBUG, "\n");
++ printk(log_level, ": ");
+
++ switch(event) {
++ case HT_EVENT_COH_EVENTS:
++ printk(log_level, "HT_EVENT_COH_EVENTS");
++ break;
++ case HT_EVENT_COH_NO_TOPOLOGY:
++ printk(log_level, "HT_EVENT_COH_NO_TOPOLOGY");
++ break;
++ case HT_EVENT_COH_LINK_EXCEED:
++ printk(log_level, "HT_EVENT_COH_LINK_EXCEED");
++ break;
++ case HT_EVENT_COH_FAMILY_FEUD:
++ printk(log_level, "HT_EVENT_COH_FAMILY_FEUD");
++ break;
++ case HT_EVENT_COH_NODE_DISCOVERED:
++ {
++ printk(log_level, "HT_EVENT_COH_NODE_DISCOVERED");
++ sHtEventCohNodeDiscovered *evt = (sHtEventCohNodeDiscovered*)pEventData0;
++ printk(log_level, ": node %d link %d new node: %d",
++ evt->node, evt->link, evt->newNode);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_COH_MPCAP_MISMATCH:
++ printk(log_level, "HT_EVENT_COH_MPCAP_MISMATCH");
++ break;
++ case HT_EVENT_NCOH_EVENTS:
++ printk(log_level, "HT_EVENT_NCOH_EVENTS");
++ break;
++ case HT_EVENT_NCOH_BUID_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_BUID_EXCEED");
++ break;
++ case HT_EVENT_NCOH_LINK_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_LINK_EXCEED");
++ break;
++ case HT_EVENT_NCOH_BUS_MAX_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_BUS_MAX_EXCEED");
++ break;
++ case HT_EVENT_NCOH_CFG_MAP_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_CFG_MAP_EXCEED");
++ break;
++ case HT_EVENT_NCOH_DEVICE_FAILED:
++ {
++ printk(log_level, "HT_EVENT_NCOH_DEVICE_FAILED");
++ sHtEventNcohDeviceFailed *evt = (sHtEventNcohDeviceFailed*)pEventData0;
++ printk(log_level, ": node %d link %d depth: %d attemptedBUID: %d",
++ evt->node, evt->link, evt->depth, evt->attemptedBUID);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_NCOH_AUTO_DEPTH:
++ {
++ printk(log_level, "HT_EVENT_NCOH_AUTO_DEPTH");
++ sHtEventNcohAutoDepth *evt = (sHtEventNcohAutoDepth*)pEventData0;
++ printk(log_level, ": node %d link %d depth: %d",
++ evt->node, evt->link, evt->depth);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_OPT_EVENTS:
++ printk(log_level, "HT_EVENT_OPT_EVENTS");
++ break;
++ case HT_EVENT_OPT_REQUIRED_CAP_RETRY:
++ printk(log_level, "HT_EVENT_OPT_REQUIRED_CAP_RETRY");
++ break;
++ case HT_EVENT_OPT_REQUIRED_CAP_GEN3:
++ printk(log_level, "HT_EVENT_OPT_REQUIRED_CAP_GEN3");
++ break;
++ case HT_EVENT_HW_EVENTS:
++ printk(log_level, "HT_EVENT_HW_EVENTS");
++ break;
++ case HT_EVENT_HW_SYNCHFLOOD:
++ printk(log_level, "HT_EVENT_HW_SYNCHFLOOD");
++ break;
++ case HT_EVENT_HW_HTCRC:
++ printk(log_level, "HT_EVENT_HW_HTCRC");
++ break;
++ default:
++ printk(log_level, "HT_EVENT_UNKNOWN");
++ break;
++ }
++ printk(log_level, "\n");
++
++ if (dump_event_detail) {
++ printk(BIOS_DEBUG, " event class: %02x\n event: %04x\n data: ", evtClass, event);
++
++ for (i = 0; i < *pEventData0; i++) {
++ printk(BIOS_DEBUG, " %02x ", *(pEventData0 + i));
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
+ }
+
+ /**
+@@ -208,9 +324,10 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ for (node = 0; node < node_count; node++) {
+ f3xe8 = pci_read_config32(NODE_PCI(node, 3), 0xe8);
+ uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
+- printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link\n", node, internal_node_number);
++ printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link", node, internal_node_number);
+ if (internal_node_number == 0) {
+ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x98:0xd8) & 0x1;
++ printk(BIOS_DEBUG, " (L3 connected: %d)\n", package_link_3_connected);
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4);
+@@ -232,15 +349,21 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ }
+ } else if (internal_node_number == 1) {
+ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xf8:0xb8) & 0x1;
++ printk(BIOS_DEBUG, " (L3 connected: %d)\n", package_link_3_connected);
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4);
+ dword &= ~0x77000000;
+ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4, dword);
+ /* Set Ganged to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174);
++ /* WARNING
++ * The Family 15h BKDG states that 0x18c should be set,
++ * however this is in error. 0x17c is the correct control
++ * register (sublink 0) for these processors...
++ */
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x17c:0x174);
+ dword |= 0x00000001;
+- pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x17c:0x174, dword);
+ } else {
+ /* Set ConnDly to 1 */
+ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 55cdd24..b7c8e83 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -5430,6 +5430,7 @@ static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc
+ cpu_divisor = (0x1 << cpu_did);
+ pMCTstat->TSCFreq = (100 * (cpu_fid + 0x10)) / cpu_divisor;
+
++ printk(BIOS_DEBUG, "mct_InitialMCT_D: mct_ForceNBPState0_En_Fam15\n");
+ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
+ } else {
+ /* K10 BKDG v3.62 section 2.8.9.2 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0090-northbridge-amd-amdfam10-Add-probe-filter-support.patch b/resources/libreboot/patch/kgpe-d16/0090-northbridge-amd-amdfam10-Add-probe-filter-support.patch
new file mode 100644
index 00000000..66897112
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0090-northbridge-amd-amdfam10-Add-probe-filter-support.patch
@@ -0,0 +1,212 @@
+From 92b74cb17d6494327b0fb83af52769aaeef0b083 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:06:39 -0500
+Subject: [PATCH 090/146] northbridge/amd/amdfam10: Add probe filter support
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 13 +++
+ src/northbridge/amd/amdfam10/northbridge.c | 144 +++++++++++++++++++++++++++-
+ 2 files changed, 156 insertions(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index e7f146f..f716e07 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -445,6 +445,19 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+
+ cpuSetAMDMSR(id.nodeid);
+
++ /* Set up probe filter support */
++ if (is_gt_rev_d()) {
++ uint8_t node_count;
++ node_count = (pci_read_config32(NODE_PCI(id.nodeid, 0), 0x60) >> 4) & 0x7;
++ node_count++;
++
++ if (node_count > 1) {
++ msr_t msr = rdmsr(BU_CFG2_MSR);
++ msr.hi |= 1 << (42 - 32);
++ wrmsr(BU_CFG2_MSR, msr);
++ }
++ }
++
+ #if CONFIG_SET_FIDVID
+ #if CONFIG_LOGICAL_CPUS && CONFIG_SET_FIDVID_CORE0_ONLY
+ // Run on all AP for proper FID/VID setup.
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 7cbb732..2c8ee08 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -30,10 +30,13 @@
+ #include <lib.h>
+ #include <smbios.h>
+ #include <cpu/cpu.h>
++#include <delay.h>
+
+ #include <cpu/x86/lapic.h>
++#include <cpu/x86/cache.h>
+ #include <cpu/amd/mtrr.h>
+ #include <cpu/amd/amdfam10_sysconf.h>
++#include <cpu/amd/model_10xxx_msr.h>
+ #include <cpu/amd/model_10xxx/ram_calc.h>
+
+ #if CONFIG_LOGICAL_CPUS
+@@ -1523,7 +1526,7 @@ static void cpu_bus_scan(device_t dev)
+ if(i>=32) {
+ busn--;
+ devn-=32;
+- pbus = pci_domain->link_list->next);
++ pbus = pci_domain->link_list->next;
+ }
+ #endif
+
+@@ -1643,8 +1646,147 @@ static void cpu_bus_scan(device_t dev)
+ }
+ }
+
++static void detect_and_enable_probe_filter(device_t dev)
++{
++ uint32_t dword;
++
++ uint8_t fam15h = 0;
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ unsigned nb_cfg_54;
++ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
++
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++
++ if (is_fam15h()) {
++ /* Family 15h or later */
++ fam15h = 1;
++ nb_cfg_54 = 1;
++ }
++
++ if ((model >= 0x8) || fam15h)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
++ if (rev_gte_d && (sysconf.nodes > 1)) {
++ /* Enable the probe filter */
++ uint8_t i;
++ uint8_t pfmode = 0x0;
++
++ uint32_t f3x58[MAX_NODES_SUPPORTED];
++ uint32_t f3x5c[MAX_NODES_SUPPORTED];
++
++ printk(BIOS_DEBUG, "Enabling probe filter\n");
++
++ /* Disable L3 and DRAM scrubbers and configure system for probe filter support */
++ for (i = 0; i < sysconf.nodes; i++) {
++ device_t f2x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 2));
++ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
++
++ f3x58[i] = pci_read_config32(f3x_dev, 0x58);
++ f3x5c[i] = pci_read_config32(f3x_dev, 0x5c);
++ pci_write_config32(f3x_dev, 0x58, f3x58[i] & ~((0x1f << 24) | 0x1f));
++ pci_write_config32(f3x_dev, 0x5c, f3x5c[i] & ~0x1);
++
++ dword = pci_read_config32(f2x_dev, 0x1b0);
++ dword &= ~(0x7 << 8); /* CohPrefPrbLmt = 0x0 */
++ pci_write_config32(f2x_dev, 0x1b0, dword);
++
++ msr_t msr = rdmsr_amd(BU_CFG2_MSR);
++ msr.hi |= 1 << (42 - 32);
++ wrmsr_amd(BU_CFG2_MSR, msr);
++
++ if (is_fam15h()) {
++ uint8_t subcache_size = 0x0;
++ uint8_t pref_so_repl = 0x0;
++ uint32_t f3x1c4 = pci_read_config32(f3x_dev, 0x1c4);
++ if ((f3x1c4 & 0xffff) == 0xcccc) {
++ subcache_size = 0x1;
++ pref_so_repl = 0x2;
++ pfmode = 0x3;
++ } else {
++ pfmode = 0x2;
++ }
++
++ dword = pci_read_config32(f3x_dev, 0x1d4);
++ dword |= 0x1 << 29; /* PFLoIndexHashEn = 0x1 */
++ dword &= ~(0x3 << 20); /* PFPreferredSORepl = pref_so_repl */
++ dword |= (pref_so_repl & 0x3) << 20;
++ dword |= 0x1 << 17; /* PFWayHashEn = 0x1 */
++ dword |= 0xf << 12; /* PFSubCacheEn = 0xf */
++ dword &= ~(0x3 << 10); /* PFSubCacheSize3 = subcache_size */
++ dword |= (subcache_size & 0x3) << 10;
++ dword &= ~(0x3 << 8); /* PFSubCacheSize2 = subcache_size */
++ dword |= (subcache_size & 0x3) << 8;
++ dword &= ~(0x3 << 6); /* PFSubCacheSize1 = subcache_size */
++ dword |= (subcache_size & 0x3) << 6;
++ dword &= ~(0x3 << 4); /* PFSubCacheSize0 = subcache_size */
++ dword |= (subcache_size & 0x3) << 4;
++ dword &= ~(0x3 << 2); /* PFWayNum = 0x2 */
++ dword |= 0x2 << 2;
++ pci_write_config32(f3x_dev, 0x1d4, dword);
++ } else {
++ pfmode = 0x2;
++
++ dword = pci_read_config32(f3x_dev, 0x1d4);
++ dword |= 0x1 << 29; /* PFLoIndexHashEn = 0x1 */
++ dword &= ~(0x3 << 20); /* PFPreferredSORepl = 0x2 */
++ dword |= 0x2 << 20;
++ dword |= 0xf << 12; /* PFSubCacheEn = 0xf */
++ dword &= ~(0x3 << 10); /* PFSubCacheSize3 = 0x0 */
++ dword &= ~(0x3 << 8); /* PFSubCacheSize2 = 0x0 */
++ dword &= ~(0x3 << 6); /* PFSubCacheSize1 = 0x0 */
++ dword &= ~(0x3 << 4); /* PFSubCacheSize0 = 0x0 */
++ dword &= ~(0x3 << 2); /* PFWayNum = 0x2 */
++ dword |= 0x2 << 2;
++ pci_write_config32(f3x_dev, 0x1d4, dword);
++ }
++ }
++
++ udelay(40);
++
++ disable_cache();
++ asm("wbinvd");
++ for (i = 0; i < sysconf.nodes; i++) {
++ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
++
++ dword = pci_read_config32(f3x_dev, 0x1c4);
++ dword |= (0x1 << 31); /* L3TagInit = 1 */
++ pci_write_config32(f3x_dev, 0x1c4, dword);
++ do {
++ } while (pci_read_config32(f3x_dev, 0x1c4) & (0x1 << 31));
++
++ dword = pci_read_config32(f3x_dev, 0x1d4);
++ dword &= ~0x3; /* PFMode = pfmode */
++ dword |= pfmode & 0x3;
++ pci_write_config32(f3x_dev, 0x1d4, dword);
++ do {
++ } while (!(pci_read_config32(f3x_dev, 0x1d4) & (0x1 << 19)));
++ }
++ enable_cache();
++
++ /* Reenable L3 and DRAM scrubbers */
++ for (i = 0; i < sysconf.nodes; i++) {
++ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
++
++ pci_write_config32(f3x_dev, 0x58, f3x58[i]);
++ pci_write_config32(f3x_dev, 0x5c, f3x5c[i]);
++ }
++
++ }
++}
++
+ static void cpu_bus_init(device_t dev)
+ {
++ detect_and_enable_probe_filter(dev);
+ initialize_cpus(dev->link_list);
+ #if CONFIG_AMD_SB_CIMX
+ sb_After_Pci_Init();
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0091-cpu-amd-model_10xxx-Bring-initial-HT-register-config.patch b/resources/libreboot/patch/kgpe-d16/0091-cpu-amd-model_10xxx-Bring-initial-HT-register-config.patch
new file mode 100644
index 00000000..953f0a15
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0091-cpu-amd-model_10xxx-Bring-initial-HT-register-config.patch
@@ -0,0 +1,246 @@
+From 7d6c926721b9cc634841b385cd48ae38a146e875 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:18:29 -0500
+Subject: [PATCH 091/146] cpu/amd/model_10xxx: Bring initial HT register
+ configuration in line with BKDG
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 168 ++++++++++++++++++++++++++++--------
+ 1 file changed, 133 insertions(+), 35 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 513d169..1080cfc 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -388,44 +388,140 @@ static const struct {
+ [2] SyncOnUcEccEn = 1 */
+
+ /* XBAR buffer settings */
+- { 3, 0x6c, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x00018052, 0x700780f7 },
++ { 3, 0x6c, AMD_FAM10_ALL & ~(AMD_DR_Dx), AMD_PTYPE_ALL,
++ 0x00018052, 0x700780f7 }, /* IsocRspDBC = 0x0,
++ UpRspDBC = 0x1,
++ DatBuf24 = 0x1,
++ DnRspDBC = 0x1,
++ DnReqDBC = 0x1,
++ UpReqDBC = 0x2 */
++
++ /* XBAR buffer settings */
++ { 3, 0x6c, AMD_DR_Dx, AMD_PTYPE_ALL,
++ 0x00028052, 0x700780f7 }, /* IsocRspDBC = 0x0,
++ UpRspDBC = 0x2,
++ DatBuf24 = 0x1,
++ DnRspDBC = 0x1,
++ DnReqDBC = 0x1,
++ UpReqDBC = 0x2 */
+
+ /* XBAR buffer settings */
+ { 3, 0x6c, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+- 0x10010052, 0x700700f7 },
++ 0x10010052, 0x700700f7 }, /* IsocRspDBC = 0x1,
++ UpRspDBC = 0x1,
++ DnRspDBC = 0x1,
++ DnReqDBC = 0x1,
++ UpReqDBC = 0x2 */
+
+ /* Errata 281 Workaround */
+- { 3, 0x6C, ( AMD_DR_B0 | AMD_DR_B1),
++ { 3, 0x6c, (AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00010094, 0x700780F7 },
+
+- { 3, 0x6C, AMD_FAM10_ALL, AMD_PTYPE_UMA,
++ { 3, 0x6c, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x60018051, 0x700780F7 },
+
+- { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x00041153, 0x777777F7 },
++ { 3, 0x70, AMD_FAM10_ALL & ~(AMD_DR_Dx), AMD_PTYPE_ALL,
++ 0x00041153, 0x777777f7 }, /* IsocRspCBC = 0x0,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x0,
++ UpRspCBC = 0x4,
++ DnPreqCBC = 0x1,
++ UpPreqCBC = 0x1,
++ DnRspCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x3 */
++
++ { 3, 0x70, AMD_DR_Dx, AMD_PTYPE_ALL,
++ 0x00051153, 0x777777f7 }, /* IsocRspCBC = 0x0,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x0,
++ UpRspCBC = 0x5,
++ DnPreqCBC = 0x1,
++ UpPreqCBC = 0x1,
++ DnRspCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x3 */
+
+ { 3, 0x70, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+- 0x10171155, 0x777777f7 },
++ 0x10171155, 0x777777f7 }, /* IsocRspCBC = 0x1,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x1,
++ UpRspCBC = 0x7,
++ DnPreqCBC = 0x1,
++ UpPreqCBC = 0x1,
++ DnRspCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x5 */
+
+ { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+- 0x61221151, 0x777777F7 },
++ 0x61221151, 0x777777f7 }, /* IsocRspCBC = 0x6,
++ IsocPreqCBC = 0x1,
++ IsocReqCBC = 0x2,
++ UpRspCBC = 0x2,
++ DnPreqCBC = 0x1,
++ UpPreqCBC = 0x1,
++ DnRspCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x1 */
++
++ { 3, 0x74, AMD_FAM10_ALL, ~AMD_PTYPE_UMA,
++ 0x00081111, 0xf7ff7777 }, /* DRReqCBC = 0x0,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x0,
++ ProbeCBC = 0x8,
++ DnPreqCBC = 0x1,
++ UpPreqCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x1 */
+
+ { 3, 0x74, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+- 0x00080101, 0x000F7777 },
++ 0x00480101, 0xf7ff7777 }, /* DRReqCBC = 0x0,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x4,
++ ProbeCBC = 0x8,
++ DnPreqCBC = 0x0,
++ UpPreqCBC = 0x1,
++ DnReqCBC = 0x0,
++ UpReqCBC = 0x1 */
+
+ { 3, 0x74, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+- 0x00172111, 0x77ff7777 },
+-
+- { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+- 0x00090914, 0x707FFF1F },
++ 0x00172111, 0xf7ff7777 }, /* DRReqCBC = 0x0,
++ IsocPreqCBC = 0x0,
++ IsocReqCBC = 0x1,
++ ProbeCBC = 0x7,
++ DnPreqCBC = 0x2,
++ UpPreqCBC = 0x1,
++ DnReqCBC = 0x1,
++ UpReqCBC = 0x1 */
++
++ { 3, 0x7c, AMD_FAM10_ALL & ~(AMD_DR_Dx), AMD_PTYPE_ALL,
++ 0x00090914, 0x707fff1f }, /* XBar2SriFreeListCBInc = 0x0,
++ Sri2XbarFreeRspDBC = 0x0,
++ Sri2XbarFreeXreqDBC = 0x9,
++ Sri2XbarFreeRspCBC = 0x0,
++ Sri2XbarFreeXreqCBC = 0x9,
++ Xbar2SriFreeListCBC = 0x14 */
++
++ { 3, 0x7c, AMD_DR_Dx, AMD_PTYPE_ALL,
++ 0x00090a18, 0x707fff1f }, /* XBar2SriFreeListCBInc = 0x0,
++ Sri2XbarFreeRspDBC = 0x0,
++ Sri2XbarFreeXreqDBC = 0x9,
++ Sri2XbarFreeRspCBC = 0x0,
++ Sri2XbarFreeXreqCBC = 0x9,
++ Xbar2SriFreeListCBC = 0x14 */
+
+ /* Errata 281 Workaround */
+ { 3, 0x7C, ( AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00144514, 0x707FFF1F },
+
+- { 3, 0x7C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+- 0x040d0f16, 0x07ffff1f },
++ { 3, 0x7c, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x040d0f16, 0x77ffff1f }, /* XBar2SriFreeListCBInc = 0x0,
++ SrqExtFreeListBC = 0x8,
++ Sri2XbarFreeRspDBC = 0x0,
++ Sri2XbarFreeXreqDBC = 0xd,
++ Sri2XbarFreeRspCBC = 0x0,
++ Sri2XbarFreeXreqCBC = 0xf,
++ Xbar2SriFreeListCBC = 0x16 */
+
+ { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00070814, 0x007FFF1F },
+@@ -647,18 +743,16 @@ static const struct {
+ 0x00004400, 0x00006400 }, /* HT_PHY_DLL_REG */
+
+ { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+- 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+- completeness */
++ 0x0000005a, 0x000000ff }, /* Use common "safe" setting for K10 */
+
+ { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+- 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+- completeness */
++ 0x0000005a, 0x000000ff }, /* Use common "safe" setting for K10 */
+
+ { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+- 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++ 0x0000006d, 0x000000ff }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+ { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+- 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++ 0x0000006d, 0x000000ff }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+ /* Link Phy Receiver Loop Filter Registers */
+ { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+@@ -681,24 +775,29 @@ static const struct {
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+ [20:16] RttIndex = 04h */
+
+-/* FIXME
+- * Causes lockups for some reason when more than one package is installed
+- * Debug and reactivate!
+- */
+-// #if 0
+ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+- 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+- completeness */
++ 0x00000a2a, 0x000000ff }, /* P0RcvRdPtr = 0xa,
++ P0XmtRdPtr = 0x2
++ P1RcvRdPtr = 0xa
++ P1XmtRdPtr = 0x0 */
+
+ { 0xDF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+- 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+- completeness */
++ 0x00000a2a, 0x000000ff }, /* P0RcvRdPtr = 0xa,
++ P0XmtRdPtr = 0x2
++ P1RcvRdPtr = 0xa
++ P1XmtRdPtr = 0x0 */
+
+ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+- 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++ 0x00000d4d, 0x000000ff }, /* P0RcvRdPtr = 0xd,
++ P0XmtRdPtr = 0x4
++ P1RcvRdPtr = 0xd
++ P1XmtRdPtr = 0x0 */
+
+ { 0xDF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+- 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
++ 0x00000d4d, 0x000000ff }, /* P0RcvRdPtr = 0xd,
++ P0XmtRdPtr = 0x4
++ P1RcvRdPtr = 0xd
++ P1XmtRdPtr = 0x0 */
+
+ /* Link Phy Receiver Loop Filter Registers */
+ { 0xD1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+@@ -719,6 +818,5 @@ static const struct {
+
+ { 0xC0, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+- [20:16] RttIndex = 04h */
+-// #endif
++ [20:16] RttIndex = 04h */
+ };
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0092-northbridge-amd-amdmct-mct_ddr3-Move-K10D-configurat.patch b/resources/libreboot/patch/kgpe-d16/0092-northbridge-amd-amdmct-mct_ddr3-Move-K10D-configurat.patch
new file mode 100644
index 00000000..2c1a411e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0092-northbridge-amd-amdmct-mct_ddr3-Move-K10D-configurat.patch
@@ -0,0 +1,334 @@
+From 4875c65838b25b4338a023111fc667e9a9f314ca Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:23:02 -0500
+Subject: [PATCH 092/146] northbridge/amd/amdmct/mct_ddr3: Move K10D
+ configuration into separate file
+
+---
+ src/northbridge/amd/amdfam10/raminit_amdmct.c | 1 +
+ src/northbridge/amd/amdmct/amddefs.h | 5 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 131 +++++++++++-------------
+ src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c | 4 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctprod.c | 65 ++++++++++++
+ 5 files changed, 130 insertions(+), 76 deletions(-)
+ create mode 100644 src/northbridge/amd/amdmct/mct_ddr3/mctprod.c
+
+diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+index 4962c2a..ebd22cd 100644
+--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
++++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
+@@ -370,6 +370,7 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t highest_rank_count, uint8
+ #include "../amdmct/mct_ddr3/mctdqs_d.c"
+ #include "../amdmct/mct_ddr3/mctsrc.c"
+ #include "../amdmct/mct_ddr3/mctsdi.c"
++#include "../amdmct/mct_ddr3/mctprod.c"
+ #include "../amdmct/mct_ddr3/mctproc.c"
+ #include "../amdmct/mct_ddr3/mctprob.c"
+ #include "../amdmct/mct_ddr3/mcthwl.c"
+diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h
+index 7aa4698..60d3c16 100644
+--- a/src/northbridge/amd/amdmct/amddefs.h
++++ b/src/northbridge/amd/amdmct/amddefs.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -115,7 +116,7 @@
+ /*
+ * CPU HT PHY REGISTERS, FIELDS, AND MASKS
+ */
+-#define HTPHY_OFFSET_MASK 0xE00001FF
++#define HTPHY_OFFSET_MASK 0xE000FFFF
+ #define HTPHY_WRITE_CMD 0x40000000
+ #define HTPHY_IS_COMPLETE_MASK 0x80000000
+ #define HTPHY_DIRECT_MAP 0x20000000
+@@ -164,4 +165,4 @@
+ #define AMD_PKGTYPE_S1gX 2
+ #define AMD_PKGTYPE_G34 3
+ #define AMD_PKGTYPE_ASB2 4
+-#define AMD_PKGTYPE_C32 5
+\ No newline at end of file
++#define AMD_PKGTYPE_C32 5
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index b7c8e83..4d8f3d0 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -182,6 +182,7 @@ static void SyncSetting(struct DCTStatStruc *pDCTstat);
+ static uint8_t crcCheck(struct DCTStatStruc *pDCTstat, uint8_t dimm);
+ static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
+ static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
++static void mct_ExtMCTConfig_Dx(struct DCTStatStruc *pDCTstat);
+
+ static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
+ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+@@ -2670,13 +2671,11 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+- /* Configure and enable prefetchers */
+- if (is_fam15h())
+- dword = 0x0ce00f41; /* BKDG recommended */
+- else
+- dword = 0x0fe40fc0; /* BKDG recommended */
+- dword |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
+- Set_NB32(pDCTstat->dev_dct, 0x11c, dword);
++ /* Enable prefetchers */
++ dword = Get_NB32(pDCTstat->dev_dct, 0x11c); /* Memory Controller Configuration High */
++ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
++ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, dword); /* Memory Controller Configuration High */
+ }
+ }
+
+@@ -4914,31 +4913,33 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
+ Get_TrwtTO(pMCTstat, pDCTstat, dct);
+ Get_TrwtWB(pMCTstat, pDCTstat);
+
+- reg = 0x8C; /* Dram Timing Hi */
+- val = Get_NB32_DCT(dev, dct, reg);
+- val &= 0xffff0300;
+- dword = pDCTstat->TrwtTO;
+- val |= dword << 4;
+- dword = pDCTstat->Twrrd & 3;
+- val |= dword << 10;
+- dword = pDCTstat->Twrwr & 3;
+- val |= dword << 12;
+- dword = pDCTstat->Trdrd & 3;
+- val |= dword << 14;
+- dword = pDCTstat->TrwtWB;
+- val |= dword;
+- Set_NB32_DCT(dev, dct, reg, val);
+-
+- reg = 0x78;
+- val = Get_NB32_DCT(dev, dct, reg);
+- val &= 0xFFFFC0FF;
+- dword = pDCTstat->Twrrd >> 2;
+- val |= dword << 8;
+- dword = pDCTstat->Twrwr >> 2;
+- val |= dword << 10;
+- dword = pDCTstat->Trdrd >> 2;
+- val |= dword << 12;
+- Set_NB32_DCT(dev, dct, reg, val);
++ if (!is_fam15h()) {
++ reg = 0x8c; /* Dram Timing Hi */
++ val = Get_NB32_DCT(dev, dct, reg);
++ val &= 0xffff0300;
++ dword = pDCTstat->TrwtTO;
++ val |= dword << 4;
++ dword = pDCTstat->Twrrd & 3;
++ val |= dword << 10;
++ dword = pDCTstat->Twrwr & 3;
++ val |= dword << 12;
++ dword = (pDCTstat->Trdrd - 0x3) & 3;
++ val |= dword << 14;
++ dword = pDCTstat->TrwtWB;
++ val |= dword;
++ Set_NB32_DCT(dev, dct, reg, val);
++
++ reg = 0x78;
++ val = Get_NB32_DCT(dev, dct, reg);
++ val &= 0xffffc0ff;
++ dword = pDCTstat->Twrrd >> 2;
++ val |= dword << 8;
++ dword = pDCTstat->Twrwr >> 2;
++ val |= dword << 10;
++ dword = (pDCTstat->Trdrd - 0x3) >> 2;
++ val |= dword << 12;
++ Set_NB32_DCT(dev, dct, reg, val);
++ }
+ }
+
+ static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
+@@ -4949,6 +4950,8 @@ static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
+ Trdrd = ((int8_t)(pDCTstat->DqsRcvEnGrossMax - pDCTstat->DqsRcvEnGrossMin) >> 1) + 1;
+ if (Trdrd > 8)
+ Trdrd = 8;
++ if (Trdrd < 3)
++ Trdrd = 3;
+ pDCTstat->Trdrd = Trdrd;
+ }
+
+@@ -5259,47 +5262,31 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->NodePresent) {
+ mct_PhyController_Config(pMCTstat, pDCTstat, 0);
+ mct_PhyController_Config(pMCTstat, pDCTstat, 1);
+- }
+- if (!(pDCTstat->LogicalCPUID & AMD_DR_Dx)) { /* mct_checkForDxSupport */
+- mct_ExtMCTConfig_Cx(pDCTstat);
+- mct_ExtMCTConfig_Bx(pDCTstat);
+- } else { /* For Dx CPU */
+- val = 0x0CE00F00 | 1 << 29/* FlushWrOnStpGnt */;
+- if (!(pDCTstat->GangedMode))
+- val |= 0x20; /* MctWrLimit = 8 for Unganged mode */
+- else
+- val |= 0x40; /* MctWrLimit = 16 for ganged mode */
+- Set_NB32(pDCTstat->dev_dct, 0x11C, val);
+-
+- val = Get_NB32(pDCTstat->dev_dct, 0x1B0);
+- val &= 0xFFFFF8C0;
+- val |= 0x101; /* BKDG recommended settings */
+- val |= 0x0FC00000; /* Agesa V5 */
+- if (!(pDCTstat->GangedMode))
+- val |= 1 << 12;
+- else
+- val &= ~(1 << 12);
+
+- val &= 0x0FFFFFFF;
+ if (!is_fam15h()) {
+- switch (pDCTstat->Speed) {
+- case 4:
+- val |= 0x50000000; /* 5 for DDR800 */
+- break;
+- case 5:
+- val |= 0x60000000; /* 6 for DDR1066 */
+- break;
+- case 6:
+- val |= 0x80000000; /* 8 for DDR800 */
+- break;
+- default:
+- val |= 0x90000000; /* 9 for DDR1600 */
+- break;
+- }
+- }
+- Set_NB32(pDCTstat->dev_dct, 0x1B0, val);
++ /* Family 10h CPUs */
++ mct_ExtMCTConfig_Cx(pDCTstat);
++ mct_ExtMCTConfig_Bx(pDCTstat);
++ mct_ExtMCTConfig_Dx(pDCTstat);
++ } else {
++ /* Family 15h CPUs */
++ val = 0x0ce00f00 | 0x1 << 29; /* FlushWrOnStpGnt */
++ val |= 0x10 << 2; /* MctWrLimit = 16 */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, val);
++
++ val = Get_NB32(pDCTstat->dev_dct, 0x1b0);
++ val &= ~0x3; /* AdapPrefMissRatio = 0x1 */
++ val |= 0x1;
++ val &= ~(0x3 << 2); /* AdapPrefPositiveStep = 0x0 */
++ val &= ~(0x3 << 4); /* AdapPrefNegativeStep = 0x0 */
++ val &= ~(0x7 << 8); /* CohPrefPrbLmt = 0x1 */
++ val |= (0x1 << 8);
++ val |= (0x1 << 12); /* EnSplitDctLimits = 0x1 */
++ val |= (0x7 << 22); /* PrefFourConf = 0x7 */
++ val |= (0x7 << 25); /* PrefFiveConf = 0x7 */
++ val &= ~(0xf << 28); /* DcqBwThrotWm = 0x0 */
++ Set_NB32(pDCTstat->dev_dct, 0x1b0, val);
+
+- if (is_fam15h()) {
+ uint8_t wm1;
+ uint8_t wm2;
+
+@@ -5330,11 +5317,11 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ break;
+ }
+
+- val = Get_NB32(pDCTstat->dev_dct, 0x1B4);
++ val = Get_NB32(pDCTstat->dev_dct, 0x1b4);
+ val &= ~(0x3ff);
+ val |= ((wm2 & 0x1f) << 5);
+ val |= (wm1 & 0x1f);
+- Set_NB32(pDCTstat->dev_dct, 0x1B4, val);
++ Set_NB32(pDCTstat->dev_dct, 0x1b4, val);
+ }
+ }
+ }
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
+index a1cdfa6..41f0946 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
+@@ -102,7 +102,7 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ BitDelta = bsf(AddrHiMask) - bsf(AddrLoMask);
+
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel++) {
+- reg = 0x40+(ChipSel<<2); /*Dram CS Base 0 */
++ reg = 0x40 + (ChipSel<<2); /* Dram CS Base 0 */
+ val = Get_NB32_DCT(dev, dct, reg);
+ if (val & 3) {
+ val_lo = val & AddrLoMask;
+@@ -118,7 +118,7 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ if(ChipSel & 1)
+ continue;
+
+- reg = 0x60 + ((ChipSel>>1)<<2); /*Dram CS Mask 0 */
++ reg = 0x60 + ((ChipSel>>1)<<2); /* Dram CS Mask 0 */
+ val = Get_NB32_DCT(dev, dct, reg);
+ val_lo = val & AddrLoMask;
+ val_hi = val & AddrHiMask;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctprod.c b/src/northbridge/amd/amdmct/mct_ddr3/mctprod.c
+new file mode 100644
+index 0000000..2b62d4c
+--- /dev/null
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctprod.c
+@@ -0,0 +1,65 @@
++/*
++ * This file is part of the coreboot project.
++ *
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
++ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc.
++ */
++
++void mct_ExtMCTConfig_Dx(struct DCTStatStruc *pDCTstat)
++{
++ uint32_t dword;
++
++ if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
++ dword = 0x0ce00f00 | 0x1 << 29; /* FlushWrOnStpGnt */
++ if (!(pDCTstat->GangedMode))
++ dword |= 0x18 << 2; /* MctWrLimit = 0x18 for unganged mode */
++ else
++ dword |= 0x10 << 2; /* MctWrLimit = 0x10 for ganged mode */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, dword);
++
++ dword = Get_NB32(pDCTstat->dev_dct, 0x1b0);
++ dword &= ~0x3; /* AdapPrefMissRatio = 0x1 */
++ dword |= 0x1;
++ dword &= ~(0x3 << 2); /* AdapPrefPositiveStep = 0x0 */
++ dword &= ~(0x3 << 4); /* AdapPrefNegativeStep = 0x0 */
++ dword &= ~(0x7 << 8); /* CohPrefPrbLmt = 0x1 */
++ dword |= (0x1 << 8);
++ dword |= (0x7 << 22); /* PrefFourConf = 0x7 */
++ dword |= (0x7 << 25); /* PrefFiveConf = 0x7 */
++
++ if (!(pDCTstat->GangedMode))
++ dword |= (0x1 << 12); /* EnSplitDctLimits = 0x1 */
++ else
++ dword &= ~(0x1 << 12); /* EnSplitDctLimits = 0x0 */
++
++ dword &= ~(0xf << 28); /* DcqBwThrotWm = ... */
++ switch (pDCTstat->Speed) {
++ case 4:
++ dword |= (0x5 << 28); /* ...5 for DDR800 */
++ break;
++ case 5:
++ dword |= (0x6 << 28); /* ...6 for DDR1066 */
++ break;
++ case 6:
++ dword |= (0x8 << 28); /* ...8 for DDR800 */
++ break;
++ default:
++ dword |= (0x9 << 28); /* ...9 for DDR1600 */
++ break;
++ }
++ Set_NB32(pDCTstat->dev_dct, 0x1b0, dword);
++ }
++}
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0093-mainboard-asus-kgpe-d16-Fix-I-O-link-detection.patch b/resources/libreboot/patch/kgpe-d16/0093-mainboard-asus-kgpe-d16-Fix-I-O-link-detection.patch
new file mode 100644
index 00000000..e360ec87
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0093-mainboard-asus-kgpe-d16-Fix-I-O-link-detection.patch
@@ -0,0 +1,26 @@
+From 0f4787f41f98a2f4529ec5eff5562fb52649e9d3 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:28:31 -0500
+Subject: [PATCH 093/146] mainboard/asus/kgpe-d16: Fix I/O link detection
+
+---
+ src/mainboard/asus/kgpe-d16/romstage.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index acc8308..13fb485 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -552,7 +552,8 @@ BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
+ {
+ /* Force BUID to 0 */
+ static const u8 swaplist[] = {0, 0, 0xFF, 0, 0xFF};
+- if ((node == 0) && (link == 1)) { /* BSP SB link */
++ if ((is_fam15h() && (node == 0) && (link == 1)) /* Family 15h BSP SB link */
++ || (!is_fam15h() && (node == 0) && (link == 3))) { /* Family 10h BSP SB link */
+ *List = swaplist;
+ return 1;
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0094-cpu-amd-model_10xxx-Set-northbridge-throttle-values.patch b/resources/libreboot/patch/kgpe-d16/0094-cpu-amd-model_10xxx-Set-northbridge-throttle-values.patch
new file mode 100644
index 00000000..fda94089
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0094-cpu-amd-model_10xxx-Set-northbridge-throttle-values.patch
@@ -0,0 +1,136 @@
+From c6ca802a248f85f59723fbbc14aaa0c49165dbc5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:31:17 -0500
+Subject: [PATCH 094/146] cpu/amd/model_10xxx: Set northbridge throttle values
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 21 +--------
+ src/cpu/amd/model_10xxx/model_10xxx_init.c | 66 ++++++++++++++++++++++++++++
+ 2 files changed, 67 insertions(+), 20 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index f716e07..6122acd 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -877,6 +877,7 @@ static u32 AMD_checkLinkType(u8 node, u8 link, u8 regoff)
+ else
+ linktype |= HTPHY_LINKTYPE_UNGANGED;
+ }
++
+ return linktype;
+ }
+
+@@ -972,26 +973,6 @@ void cpuSetAMDMSR(uint8_t node_id)
+ }
+ AMD_Errata298();
+
+- if (revision & AMD_FAM15_ALL) {
+- uint32_t f5x80;
+- uint8_t enabled;
+- uint8_t compute_unit_count = 0;
+- f5x80 = pci_read_config32(NODE_PCI(node_id, 5), 0x80);
+- enabled = f5x80 & 0xf;
+- if (enabled == 0x1)
+- compute_unit_count = 1;
+- if (enabled == 0x3)
+- compute_unit_count = 2;
+- if (enabled == 0x7)
+- compute_unit_count = 3;
+- if (enabled == 0xf)
+- compute_unit_count = 4;
+- msr = rdmsr(BU_CFG2);
+- msr.lo &= ~(0x3 << 6); /* ThrottleNbInterface[1:0] */
+- msr.lo |= (((compute_unit_count - 1) & 0x3) << 6);
+- wrmsr(BU_CFG2, msr);
+- }
+-
+ /* Revision C0 and above */
+ if (revision & AMD_OR_C0) {
+ uint32_t f3x1fc = pci_read_config32(NODE_PCI(node_id, 3), 0x1fc);
+diff --git a/src/cpu/amd/model_10xxx/model_10xxx_init.c b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+index 8a61f13..7319539 100644
+--- a/src/cpu/amd/model_10xxx/model_10xxx_init.c
++++ b/src/cpu/amd/model_10xxx/model_10xxx_init.c
+@@ -54,6 +54,28 @@ static inline uint8_t is_fam15h(void)
+ return fam15h;
+ }
+
++static inline uint8_t is_gt_rev_d(void)
++{
++ uint8_t fam15h = 0;
++ uint8_t rev_gte_d = 0;
++ uint32_t family;
++ uint32_t model;
++
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ if ((model >= 0x8) || fam15h)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ return rev_gte_d;
++}
++
+ static volatile uint8_t fam15h_startup_flags[MAX_NODES_SUPPORTED][MAX_CORES_SUPPORTED] = {{ 0 }};
+
+ static void model_10xxx_init(device_t dev)
+@@ -136,6 +158,50 @@ static void model_10xxx_init(device_t dev)
+ printk(BIOS_DEBUG, "siblings = %02d, ", siblings);
+ #endif
+
++ /* Set bus unit configuration */
++ if (is_fam15h()) {
++ uint32_t f5x80;
++ uint8_t enabled;
++ uint8_t compute_unit_count = 0;
++ f5x80 = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18 + id.nodeid, 5)), 0x80);
++ enabled = f5x80 & 0xf;
++ if (enabled == 0x1)
++ compute_unit_count = 1;
++ if (enabled == 0x3)
++ compute_unit_count = 2;
++ if (enabled == 0x7)
++ compute_unit_count = 3;
++ if (enabled == 0xf)
++ compute_unit_count = 4;
++ msr = rdmsr(BU_CFG2_MSR);
++ msr.lo &= ~(0x3 << 6); /* ThrottleNbInterface[1:0] */
++ msr.lo |= (((compute_unit_count - 1) & 0x3) << 6);
++ wrmsr(BU_CFG2_MSR, msr);
++ } else {
++ uint32_t f0x60;
++ uint32_t f0x160;
++ uint8_t core_count = 0;
++ uint8_t node_count = 0;
++ f0x60 = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18 + id.nodeid, 0)), 0x60);
++ core_count = (f0x60 >> 16) & 0x1f;
++ node_count = ((f0x60 >> 4) & 0x7) + 1;
++ if (is_gt_rev_d()) {
++ f0x160 = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18 + id.nodeid, 0)), 0x160);
++ core_count |= ((f0x160 >> 16) & 0x7) << 5;
++ }
++ core_count++;
++ core_count /= node_count;
++ msr = rdmsr(BU_CFG2_MSR);
++ if (is_gt_rev_d()) {
++ msr.hi &= ~(0x3 << (36 - 32)); /* ThrottleNbInterface[3:2] */
++ msr.hi |= ((((core_count - 1) >> 2) & 0x3) << (36 - 32));
++ }
++ msr.lo &= ~(0x3 << 6); /* ThrottleNbInterface[1:0] */
++ msr.lo |= (((core_count - 1) & 0x3) << 6);
++ msr.lo &= ~(0x1 << 24); /* WcPlusDis = 0 */
++ wrmsr(BU_CFG2_MSR, msr);
++ }
++
+ /* Disable Cf8ExtCfg */
+ msr = rdmsr(NB_CFG_MSR);
+ msr.hi &= ~(1 << (46 - 32));
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0095-cpu-amd-model_10xxx-Fix-incorrect-revision-detection.patch b/resources/libreboot/patch/kgpe-d16/0095-cpu-amd-model_10xxx-Fix-incorrect-revision-detection.patch
new file mode 100644
index 00000000..842202e9
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0095-cpu-amd-model_10xxx-Fix-incorrect-revision-detection.patch
@@ -0,0 +1,62 @@
+From 6212e218fbd616d7ddeb74fffd883741ba40a6d6 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:31:48 -0500
+Subject: [PATCH 095/146] cpu/amd/model_10xxx: Fix incorrect revision
+ detection
+
+---
+ src/cpu/amd/model_10xxx/fidvid.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index fe1cfb4..1d55275 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -375,7 +375,7 @@ static void recalculateVsSlamTimeSettingOnCorePre(device_t dev)
+ pci_write_config32(dev, 0xd8, dtemp);
+ }
+
+-static u32 nb_clk_did(int node, u32 cpuRev,u8 procPkg) {
++static u32 nb_clk_did(int node, uint64_t cpuRev, uint8_t procPkg) {
+ u8 link0isGen3 = 0;
+ u8 offset;
+ if (AMD_CpuFindCapability(node, 0, &offset)) {
+@@ -446,7 +446,7 @@ static u32 power_up_down(int node, u8 procPkg) {
+ return dword;
+ }
+
+-static void config_clk_power_ctrl_reg0(int node, u32 cpuRev, u8 procPkg) {
++static void config_clk_power_ctrl_reg0(int node, uint64_t cpuRev, uint8_t procPkg) {
+ device_t dev = NODE_PCI(node, 3);
+
+ /* Program fields in Clock Power/Control register0 (F3xD4) */
+@@ -471,7 +471,7 @@ static void config_clk_power_ctrl_reg0(int node, u32 cpuRev, u8 procPkg) {
+
+ }
+
+-static void config_power_ctrl_misc_reg(device_t dev,u32 cpuRev, u8 procPkg) {
++static void config_power_ctrl_misc_reg(device_t dev, uint64_t cpuRev, uint8_t procPkg) {
+ /* check PVI/SVI */
+ u32 dword = pci_read_config32(dev, 0xa0);
+
+@@ -504,7 +504,7 @@ static void config_power_ctrl_misc_reg(device_t dev,u32 cpuRev, u8 procPkg) {
+ pci_write_config32(dev, 0xa0, dword);
+ }
+
+-static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
++static void config_nb_syn_ptr_adj(device_t dev, uint64_t cpuRev) {
+ /* Note the following settings are additional from the ported
+ * function setFidVidRegs()
+ */
+@@ -526,7 +526,7 @@ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
+ pci_write_config32(dev, 0xdc, dword);
+ }
+
+-static void config_acpi_pwr_state_ctrl_regs(device_t dev, u32 cpuRev, u8 procPkg) {
++static void config_acpi_pwr_state_ctrl_regs(device_t dev, uint64_t cpuRev, uint8_t procPkg) {
+ if (is_fam15h()) {
+ /* Family 15h BKDG Rev. 3.14 D18F3x80 recommended settings */
+ pci_write_config32(dev, 0x80, 0xe20be281);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0096-northbridge-amd-amdht-Add-support-for-HT3-2.8GHz-and.patch b/resources/libreboot/patch/kgpe-d16/0096-northbridge-amd-amdht-Add-support-for-HT3-2.8GHz-and.patch
new file mode 100644
index 00000000..c14e4693
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0096-northbridge-amd-amdht-Add-support-for-HT3-2.8GHz-and.patch
@@ -0,0 +1,559 @@
+From 5f98c8dc52acec481b51efa118af100351812241 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 2 Aug 2015 21:36:24 -0500
+Subject: [PATCH 096/146] northbridge/amd/amdht: Add support for HT3 2.8GHz
+ and up link frequencies
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 8 ++-
+ src/mainboard/asus/kgpe-d16/cmos.layout | 30 ++++-----
+ src/northbridge/amd/amdht/h3ffeat.h | 6 +-
+ src/northbridge/amd/amdht/h3finit.c | 93 ++++++++++++++++-----------
+ src/northbridge/amd/amdht/h3finit.h | 18 ++++--
+ src/northbridge/amd/amdht/h3ncmn.c | 104 ++++++++++++++++++++++++-------
+ src/northbridge/amd/amdht/h3ncmn.h | 3 +-
+ 7 files changed, 177 insertions(+), 85 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 1080cfc..bff2efd 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -290,7 +290,13 @@ static const struct {
+
+ /* Link Global Retry Control Register */
+ { 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+- 0x00073900, 0x00073F00 },
++ 0x00073900, 0x00073f70 }, /* TotalRetryAttempts = 0x7,
++ HtRetryCrcDatInsDynEn = 0x1,
++ HtRetryCrcCmdPackDynEn = 0x1,
++ HtRetryCrcDatIns = 0x4,
++ HtRetryCrcCmdPack = 0x1,
++ ForceErrType = 0x0,
++ MultRetryErr = 0x0 */
+
+ /* Errata 351
+ * System software should program the Link Extended Control Registers[LS2En]
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index f705af2..ec803b6 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -125,21 +125,21 @@ enumerations
+ 10 21 42ms
+ 10 22 84ms
+ 11 0 Auto
+-11 1 2.6GHz
+-11 2 2.4GHz
+-11 3 2.2GHz
+-11 4 2.0GHz
+-11 5 1.8GHz
+-11 6 1.6GHz
+-11 7 1.4GHz
+-11 8 1.2GHz
+-11 9 1.0GHz
+-11 10 800MHz
+-11 11 600MHz
+-11 12 500MHz
+-11 13 400MHz
+-11 14 300MHz
+-11 15 200MHz
++11 1 3.2GHz
++11 2 3.0GHz
++11 3 2.8GHz
++11 4 2.6GHz
++11 5 2.4GHz
++11 6 2.2GHz
++11 7 2.0GHz
++11 8 1.8GHz
++11 9 1.6GHz
++11 10 1.4GHz
++11 11 1.2GHz
++11 12 1.0GHz
++11 13 800MHz
++11 14 600MHz
++11 15 500MHz
+ 12 0 1.5V
+ 12 1 1.35V
+ 12 2 1.25V
+diff --git a/src/northbridge/amd/amdht/h3ffeat.h b/src/northbridge/amd/amdht/h3ffeat.h
+index 5d9550b..5dc9916 100644
+--- a/src/northbridge/amd/amdht/h3ffeat.h
++++ b/src/northbridge/amd/amdht/h3ffeat.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -62,6 +63,7 @@
+ #define HTHOST_LINK_CAPABILITY_REG 0x00
+ #define HTHOST_LINK_CONTROL_REG 0x04
+ #define HTHOST_FREQ_REV_REG 0x08
++#define HTHOST_FREQ_REV_REG_2 0x1c
+ #define HT_HOST_REV_REV3 0x60
+ #define HTHOST_FEATURE_CAP_REG 0x0C
+ #define HTHOST_BUFFER_COUNT_REG 0x10
+@@ -127,10 +129,10 @@ typedef struct
+
+ /* This section is for keeping track of capabilities and possible configurations */
+ BOOL RegangCap;
+- u16 PrvFrequencyCap;
++ uint32_t PrvFrequencyCap;
+ u8 PrvWidthInCap;
+ u8 PrvWidthOutCap;
+- u16 CompositeFrequencyCap;
++ uint32_t CompositeFrequencyCap;
+
+ } sPortDescriptor;
+
+diff --git a/src/northbridge/amd/amdht/h3finit.c b/src/northbridge/amd/amdht/h3finit.c
+index 82bf885..14f348f 100644
+--- a/src/northbridge/amd/amdht/h3finit.c
++++ b/src/northbridge/amd/amdht/h3finit.c
+@@ -52,28 +52,32 @@
+ #define APIC_Base_BSP 8
+ #define APIC_Base 0x1b
+
+-#define NVRAM_LIMIT_HT_SPEED_200 0xf
+-#define NVRAM_LIMIT_HT_SPEED_300 0xe
+-#define NVRAM_LIMIT_HT_SPEED_400 0xd
+-#define NVRAM_LIMIT_HT_SPEED_500 0xc
+-#define NVRAM_LIMIT_HT_SPEED_600 0xb
+-#define NVRAM_LIMIT_HT_SPEED_800 0xa
+-#define NVRAM_LIMIT_HT_SPEED_1000 0x9
+-#define NVRAM_LIMIT_HT_SPEED_1200 0x8
+-#define NVRAM_LIMIT_HT_SPEED_1400 0x7
+-#define NVRAM_LIMIT_HT_SPEED_1600 0x6
+-#define NVRAM_LIMIT_HT_SPEED_1800 0x5
+-#define NVRAM_LIMIT_HT_SPEED_2000 0x4
+-#define NVRAM_LIMIT_HT_SPEED_2200 0x3
+-#define NVRAM_LIMIT_HT_SPEED_2400 0x2
+-#define NVRAM_LIMIT_HT_SPEED_2600 0x1
++#define NVRAM_LIMIT_HT_SPEED_200 0x12
++#define NVRAM_LIMIT_HT_SPEED_300 0x11
++#define NVRAM_LIMIT_HT_SPEED_400 0x10
++#define NVRAM_LIMIT_HT_SPEED_500 0xf
++#define NVRAM_LIMIT_HT_SPEED_600 0xe
++#define NVRAM_LIMIT_HT_SPEED_800 0xd
++#define NVRAM_LIMIT_HT_SPEED_1000 0xc
++#define NVRAM_LIMIT_HT_SPEED_1200 0xb
++#define NVRAM_LIMIT_HT_SPEED_1400 0xa
++#define NVRAM_LIMIT_HT_SPEED_1600 0x9
++#define NVRAM_LIMIT_HT_SPEED_1800 0x8
++#define NVRAM_LIMIT_HT_SPEED_2000 0x7
++#define NVRAM_LIMIT_HT_SPEED_2200 0x6
++#define NVRAM_LIMIT_HT_SPEED_2400 0x5
++#define NVRAM_LIMIT_HT_SPEED_2600 0x4
++#define NVRAM_LIMIT_HT_SPEED_2800 0x3
++#define NVRAM_LIMIT_HT_SPEED_3000 0x2
++#define NVRAM_LIMIT_HT_SPEED_3200 0x1
+ #define NVRAM_LIMIT_HT_SPEED_AUTO 0x0
+
+-static const uint16_t ht_speed_limit[16] =
+- {0xFFFF, 0x7FFF, 0x3FFF, 0x1FFF,
+- 0x0FFF, 0x07FF, 0x03FF, 0x01FF,
+- 0x00FF, 0x007F, 0x003F, 0x001F,
+- 0x000F, 0x0007, 0x0003, 0x0001};
++static const uint32_t ht_speed_limit[20] =
++ {0xFFFFF, 0xFFFFF, 0x7FFFF, 0x3FFFF,
++ 0x0FFFF, 0x07FFF, 0x03FFF, 0x01FFF,
++ 0x00FFF, 0x007FF, 0x003FF, 0x001FF,
++ 0x000FF, 0x0007F, 0x0003F, 0x0001F,
++ 0x0000F, 0x00007, 0x00003, 0x00001};
+
+ static const struct ht_speed_limit_map_t {
+ uint16_t mhz;
+@@ -95,9 +99,12 @@ static const struct ht_speed_limit_map_t {
+ {2200, NVRAM_LIMIT_HT_SPEED_2200},
+ {2400, NVRAM_LIMIT_HT_SPEED_2400},
+ {2600, NVRAM_LIMIT_HT_SPEED_2600},
++ {2800, NVRAM_LIMIT_HT_SPEED_2800},
++ {3000, NVRAM_LIMIT_HT_SPEED_3000},
++ {3200, NVRAM_LIMIT_HT_SPEED_3200},
+ };
+
+-static const uint16_t ht_speed_mhz_to_hw(uint16_t mhz)
++static const uint32_t ht_speed_mhz_to_hw(uint16_t mhz)
+ {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(ht_speed_limit_map); i++)
+@@ -456,7 +463,7 @@ static void htDiscoveryFloodFill(sMainData *pDat)
+ /* Set currentNode's NodeID field to currentNode */
+ pDat->nb->writeNodeID(currentNode, currentNode, pDat->nb);
+
+- /* Enable routing tables on currentNode*/
++ /* Enable routing tables on currentNode */
+ pDat->nb->enableRoutingTables(currentNode, pDat->nb);
+
+ for (currentLinkID = 0; currentLinkID < pDat->nb->maxLinks; currentLinkID++)
+@@ -1431,19 +1438,30 @@ static void regangLinks(sMainData *pDat)
+ static void selectOptimalWidthAndFrequency(sMainData *pDat)
+ {
+ u8 i, j;
+- u32 temp;
+- u16 cbPCBFreqLimit;
+- u16 cbPCBFreqLimit_NVRAM;
++ uint32_t temp;
++ uint32_t cbPCBFreqLimit;
++ uint32_t cbPCBFreqLimit_NVRAM;
+ u8 cbPCBABDownstreamWidth;
+ u8 cbPCBBAUpstreamWidth;
+
+- cbPCBFreqLimit_NVRAM = 0xFFFF;
++ cbPCBFreqLimit_NVRAM = 0xfffff;
+ if (get_option(&temp, "hypertransport_speed_limit") == CB_SUCCESS)
+ cbPCBFreqLimit_NVRAM = ht_speed_limit[temp & 0xf];
+
++ if (!is_fam15h()) {
++ /* FIXME
++ * By default limit frequency to 2.6 GHz as there are residual
++ * problems with HT v3.1 implementation on at least some Socket G34
++ * mainboards / Fam10h CPUs.
++ * Debug the issues and reenable this...
++ */
++ if (cbPCBFreqLimit_NVRAM > 0xffff)
++ cbPCBFreqLimit_NVRAM = 0xffff;
++ }
++
+ for (i = 0; i < pDat->TotalLinks*2; i += 2)
+ {
+- cbPCBFreqLimit = 0xFFFF; // Maximum allowed by autoconfiguration
++ cbPCBFreqLimit = 0xfffff; // Maximum allowed by autoconfiguration
+ if (pDat->HtBlock->ht_link_configuration)
+ cbPCBFreqLimit = ht_speed_mhz_to_hw(pDat->HtBlock->ht_link_configuration->ht_speed_limit);
+ cbPCBFreqLimit = min(cbPCBFreqLimit, cbPCBFreqLimit_NVRAM);
+@@ -1488,17 +1506,18 @@ static void selectOptimalWidthAndFrequency(sMainData *pDat)
+ }
+ }
+
+-
+ temp = pDat->PortList[i].PrvFrequencyCap;
+ temp &= pDat->PortList[i+1].PrvFrequencyCap;
+ temp &= cbPCBFreqLimit;
+- pDat->PortList[i].CompositeFrequencyCap = (u16)temp;
+- pDat->PortList[i+1].CompositeFrequencyCap = (u16)temp;
++ pDat->PortList[i].CompositeFrequencyCap = temp;
++ pDat->PortList[i+1].CompositeFrequencyCap = temp;
+
+ ASSERT (temp != 0);
+- for (j = 15; ; j--)
++ for (j = 19; ; j--)
+ {
+- if (temp & ((u32)1 << j))
++ if ((j == 16) || (j == 15))
++ continue;
++ if (temp & ((uint32_t)1 << j))
+ break;
+ }
+
+@@ -1642,12 +1661,14 @@ static void hammerSublinkFixup(sMainData *pDat)
+ /* Remove hiFreq from the list of valid frequencies */
+ temp = temp & ~((uint32)1 << hiFreq);
+ ASSERT (temp != 0);
+- pDat->PortList[hiIndex].CompositeFrequencyCap = (uint16)temp;
+- pDat->PortList[hiIndex+1].CompositeFrequencyCap = (uint16)temp;
++ pDat->PortList[hiIndex].CompositeFrequencyCap = temp;
++ pDat->PortList[hiIndex+1].CompositeFrequencyCap = temp;
+
+- for (k = 15; ; k--)
++ for (k = 19; ; k--)
+ {
+- if (temp & ((u32)1 << k))
++ if ((j == 16) || (j == 15))
++ continue;
++ if (temp & ((uint32_t)1 << k))
+ break;
+ }
+
+diff --git a/src/northbridge/amd/amdht/h3finit.h b/src/northbridge/amd/amdht/h3finit.h
+index 58065b3..462f3e3 100644
+--- a/src/northbridge/amd/amdht/h3finit.h
++++ b/src/northbridge/amd/amdht/h3finit.h
+@@ -53,6 +53,9 @@
+ #define HT_FREQUENCY_2200M 12
+ #define HT_FREQUENCY_2400M 13
+ #define HT_FREQUENCY_2600M 14
++#define HT_FREQUENCY_2800M 17
++#define HT_FREQUENCY_3000M 18
++#define HT_FREQUENCY_3200M 19
+
+ /* Frequency Limit equates for call backs which take a frequency supported mask. */
+ #define HT_FREQUENCY_LIMIT_200M 1
+@@ -69,6 +72,9 @@
+ #define HT_FREQUENCY_LIMIT_2200M 0x1FFF
+ #define HT_FREQUENCY_LIMIT_2400M 0x3FFF
+ #define HT_FREQUENCY_LIMIT_2600M 0x7FFF
++#define HT_FREQUENCY_LIMIT_2800M 0x3FFFF
++#define HT_FREQUENCY_LIMIT_3000M 0x7FFFF
++#define HT_FREQUENCY_LIMIT_3200M 0xFFFFF
+
+ /*
+ * Event Notify definitions
+@@ -224,7 +230,7 @@ typedef struct {
+ * @param[in] u8 Link = The Device's link number (0 or 1)
+ * @param[in,out] u8* LinkWidthIn = modify to change the Link Witdh In
+ * @param[in,out] u8* LinkWidthOut = modify to change the Link Witdh Out
+- * @param[in,out] u16* FreqCap = modify to change the link's frequency capability
++ * @param[in,out] u32* FreqCap = modify to change the link's frequency capability
+ *
+ * ---------------------------------------------------------------------------------------
+ */
+@@ -239,7 +245,7 @@ typedef struct {
+ u8 Link,
+ u8 *LinkWidthIn,
+ u8 *LinkWidthOut,
+- u16 *FreqCap
++ u32 *FreqCap
+ );
+
+ /**----------------------------------------------------------------------------------------
+@@ -262,7 +268,7 @@ typedef struct {
+ * @param[in] u8 linkB = The link on that node
+ * @param[in,out] u8* ABLinkWidthLimit = modify to change the Link Witdh In
+ * @param[in,out] u8* BALinkWidthLimit = modify to change the Link Witdh Out
+- * @param[in,out] u16* PCBFreqCap = modify to change the link's frequency capability
++ * @param[in,out] u32* PCBFreqCap = modify to change the link's frequency capability
+ *
+ * ---------------------------------------------------------------------------------------
+ */
+@@ -273,7 +279,7 @@ typedef struct {
+ u8 LinkB,
+ u8 *ABLinkWidthLimit,
+ u8 *BALinkWidthLimit,
+- u16 *PCBFreqCap
++ u32 *PCBFreqCap
+ );
+
+ /**----------------------------------------------------------------------------------------
+@@ -295,7 +301,7 @@ typedef struct {
+ * @param[in] u8 Depth = The depth in the I/O chain from the Host
+ * @param[in,out] u8* DownstreamLinkWidthLimit = modify to change the Link Witdh In
+ * @param[in,out] u8* UpstreamLinkWidthLimit = modify to change the Link Witdh Out
+- * @param[in,out] u16* PCBFreqCap = modify to change the link's frequency capability
++ * @param[in,out] u32* PCBFreqCap = modify to change the link's frequency capability
+ *
+ * ---------------------------------------------------------------------------------------
+ */
+@@ -305,7 +311,7 @@ typedef struct {
+ u8 Depth,
+ u8 *DownstreamLinkWidthLimit,
+ u8 *UpstreamLinkWidthLimit,
+- u16 *PCBFreqCap
++ u32 *PCBFreqCap
+ );
+
+ /**----------------------------------------------------------------------------------------
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index 1026d0e..e03e5eb 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -107,6 +107,28 @@ static inline uint8_t is_fam15h(void)
+ return fam15h;
+ }
+
++static inline uint8_t is_gt_rev_d(void)
++{
++ uint8_t fam15h = 0;
++ uint8_t rev_gte_d = 0;
++ uint32_t family;
++ uint32_t model;
++
++ family = model = cpuid_eax(0x80000001);
++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
++
++ if (family >= 0x6f)
++ /* Family 15h or later */
++ fam15h = 1;
++
++ if ((model >= 0x8) || fam15h)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ return rev_gte_d;
++}
++
+ /***************************************************************************//**
+ *
+ * SBDFO
+@@ -1292,7 +1314,7 @@ static u8 convertWidthToBits(u8 value, cNorthBridge *nb)
+ * @return Frequency mask
+ *
+ ******************************************************************************/
+-static u16 ht1NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
++static uint32_t ht1NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
+ {
+ /* only up to HT1 speeds */
+ return (HT_FREQUENCY_LIMIT_HT1_ONLY);
+@@ -1313,26 +1335,43 @@ static u16 ht1NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
+ * @return = Frequency mask
+ *
+ ******************************************************************************/
+-static u16 fam10NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
++static uint32_t fam10NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
+ {
+ u8 nbCOF;
+- u16 supported;
++ uint32_t supported;
+
+ nbCOF = getMinNbCOF();
+ /*
+ * nbCOF is minimum northbridge speed in hundreds of MHz.
+ * HT can not go faster than the minimum speed of the northbridge.
+ */
+- if ((nbCOF >= 6) && (nbCOF <= 26))
++ if ((nbCOF >= 6) && (nbCOF < 10))
+ {
++ /* Generation 1 HT link frequency */
+ /* Convert frequency to bit and all less significant bits,
+ * by setting next power of 2 and subtracting 1.
+ */
+- supported = ((u16)1 << ((nbCOF >> 1) + 2)) - 1;
++ supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
+ }
+- else if (nbCOF > 26)
++ else if ((nbCOF >= 10) && (nbCOF <= 32))
+ {
+- supported = HT_FREQUENCY_LIMIT_2600M;
++ /* Generation 3 HT link frequency
++ * Assume error retry is enabled on all Gen 3 links
++ */
++ if (is_gt_rev_d()) {
++ nbCOF *= 2;
++ if (nbCOF > 32)
++ nbCOF = 32;
++ }
++
++ /* Convert frequency to bit and all less significant bits,
++ * by setting next power of 2 and subtracting 1.
++ */
++ supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
++ }
++ else if (nbCOF > 32)
++ {
++ supported = HT_FREQUENCY_LIMIT_3200M;
+ }
+ /* unlikely cases, but include as a defensive measure, also avoid trick above */
+ else if (nbCOF == 4)
+@@ -1387,8 +1426,13 @@ static void gatherLinkData(sMainData *pDat, cNorthBridge *nb)
+ pDat->PortList[i].PrvWidthInCap = convertBitsToWidth((u8)temp, pDat->nb);
+
+ AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG, 31, 16, &temp);
+- pDat->PortList[i].PrvFrequencyCap = (u16)temp & 0x7FFF
+- & nb->northBridgeFreqMask(pDat->PortList[i].NodeID, pDat->nb); /* Mask off bit 15, reserved value */
++ pDat->PortList[i].PrvFrequencyCap = temp & 0x7FFF /* Mask off bit 15, reserved value */
++ & nb->northBridgeFreqMask(pDat->PortList[i].NodeID, pDat->nb);
++ if (is_gt_rev_d()) {
++ AmdPCIReadBits(linkBase + HTHOST_FREQ_REV_REG_2, 15, 1, &temp);
++ temp &= 0x7; /* Mask off reserved values */
++ pDat->PortList[i].PrvFrequencyCap |= (temp << 17);
++ }
+ }
+ else
+ {
+@@ -1445,7 +1489,7 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ {
+ u8 i;
+ SBDFO linkBase;
+- u32 temp, widthin, widthout, bits;
++ u32 temp, temp2, frequency_index, widthin, widthout, bits;
+
+ for (i = 0; i < pDat->TotalLinks*2; i++)
+ {
+@@ -1506,10 +1550,19 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ temp = pDat->PortList[i].SelFrequency;
+ if (pDat->PortList[i].Type == PORTLIST_TYPE_CPU)
+ {
+- ASSERT((temp >= HT_FREQUENCY_600M && temp <= HT_FREQUENCY_2600M)
++ ASSERT((temp >= HT_FREQUENCY_600M && temp <= HT_FREQUENCY_3200M)
+ || (temp == HT_FREQUENCY_200M) || (temp == HT_FREQUENCY_400M));
++ frequency_index = temp;
++ if (temp > 0xf) {
++ temp2 = (temp >> 4) & 0x1;
++ temp &= 0xf;
++ } else {
++ temp2 = 0x0;
++ }
++ if (is_gt_rev_d())
++ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG_2, 0, 0, &temp2);
+ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG, 11, 8, &temp);
+- if (temp > HT_FREQUENCY_1000M) /* Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 2600MHz */
++ if (frequency_index > HT_FREQUENCY_1000M) /* Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 3200MHz */
+ {
+ /* Enable for Gen3 frequencies */
+ temp = 1;
+@@ -1519,27 +1572,27 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ /* Disable for Gen1 frequencies */
+ temp = 0;
+ }
+- /* HT3 retry mode enable / disable */
+- AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
+- makePCIBusFromNode(pDat->PortList[i].NodeID),
+- makePCIDeviceFromNode(pDat->PortList[i].NodeID),
+- CPU_HTNB_FUNC_00,
+- REG_HT_LINK_RETRY0_0X130 + 4*pDat->PortList[i].Link),
+- 0, 0, &temp);
+- /* and Scrambling enable / disable */
+- AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
++ /* HT3 retry mode enable / disable */
++ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
+ makePCIBusFromNode(pDat->PortList[i].NodeID),
+ makePCIDeviceFromNode(pDat->PortList[i].NodeID),
+ CPU_HTNB_FUNC_00,
+- REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link),
+- 3, 3, &temp);
++ REG_HT_LINK_RETRY0_0X130 + 4*pDat->PortList[i].Link),
++ 0, 0, &temp);
++ /* and Scrambling enable / disable */
++ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
++ makePCIBusFromNode(pDat->PortList[i].NodeID),
++ makePCIDeviceFromNode(pDat->PortList[i].NodeID),
++ CPU_HTNB_FUNC_00,
++ REG_HT_LINK_EXT_CONTROL0_0X170 + 4*pDat->PortList[i].Link),
++ 3, 3, &temp);
+ }
+ else
+ {
+ SBDFO currentPtr;
+ BOOL isFound;
+
+- ASSERT(temp <= HT_FREQUENCY_2600M);
++ ASSERT(temp <= HT_FREQUENCY_3200M);
+ /* Write the frequency setting */
+ AmdPCIWriteBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 11, 8, &temp);
+
+@@ -1700,6 +1753,9 @@ static void fam0fWriteHTLinkCmdBufferAlloc(u8 node, u8 link, u8 req, u8 preq, u8
+ /* Probe Command Buffers */
+ temp = prb;
+ AmdPCIWriteBits(currentPtr, 15, 12, &temp);
++ /* LockBc */
++ temp = 1;
++ AmdPCIWriteBits(currentPtr, 31, 31, &temp);
+ }
+ #endif /* HT_BUILD_NC_ONLY */
+
+diff --git a/src/northbridge/amd/amdht/h3ncmn.h b/src/northbridge/amd/amdht/h3ncmn.h
+index 7f8f4d1..5795e9a 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.h
++++ b/src/northbridge/amd/amdht/h3ncmn.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -107,7 +108,7 @@ struct cNorthBridge
+ /* Public Interfaces for northbridge clients, Optimization */
+ u8 (*convertBitsToWidth)(u8 value, cNorthBridge *nb);
+ u8 (*convertWidthToBits)(u8 value, cNorthBridge *nb);
+- u16 (*northBridgeFreqMask)(u8 node, cNorthBridge *nb);
++ uint32_t (*northBridgeFreqMask)(u8 node, cNorthBridge *nb);
+ void (*gatherLinkData)(sMainData *pDat, cNorthBridge *nb);
+ void (*setLinkData)(sMainData *pDat, cNorthBridge *nb);
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0097-amd-model_10xxx-Fix-poor-performance-on-Family-15h-C.patch b/resources/libreboot/patch/kgpe-d16/0097-amd-model_10xxx-Fix-poor-performance-on-Family-15h-C.patch
new file mode 100644
index 00000000..9f21a62f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0097-amd-model_10xxx-Fix-poor-performance-on-Family-15h-C.patch
@@ -0,0 +1,122 @@
+From 4dbb7204da39a6f47acb6875538acb4880f3a49b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 19:04:49 -0500
+Subject: [PATCH 097/146] amd/model_10xxx: Fix poor performance on Family 15h
+ CPUs
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 13 +++++++---
+ src/cpu/amd/model_10xxx/init_cpus.c | 45 ++++++++++++++++++++++++++++++++---
+ 2 files changed, 52 insertions(+), 6 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index bff2efd..4868c5c 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -535,15 +535,15 @@ static const struct {
+ { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00800756, 0x00F3FFFF },
+
+- { 3, 0x140, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+- 0x00a11755, 0x00f3ffff },
+-
+ { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00C37756, 0x00F3FFFF },
+
+ { 3, 0x144, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x00000036, 0x000000FF },
+
++ { 3, 0x140, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00a11755, 0x00f3ffff },
++
+ /* Errata 281 Workaround */
+ { 3, 0x144, ( AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00000001, 0x0000000F },
+@@ -555,6 +555,13 @@ static const struct {
+ { 3, 0x148, AMD_FAM10_ALL, AMD_PTYPE_UMA,
+ 0x8000052A, 0xD5FFFFFF },
+
++ /* Core Interface Buffer Count */
++ { 3, 0x1a0, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00034004, 0x00037007 }, /* CpuToNbFreeBufCnt = 0x3,
++ L3ToSriReqCBC = 0x4,
++ L3FreeListCBC = default,
++ CpuCmdBufCnt = 0x4 */
++
+ /* ACPI Power State Control Reg1 */
+ { 3, 0x80, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0xE6002200, 0xFFFFFFFF },
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 6122acd..15ed259 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -847,8 +847,9 @@ static BOOL AMD_CpuFindCapability(u8 node, u8 cap_count, u8 * offset)
+ */
+ static u32 AMD_checkLinkType(u8 node, u8 link, u8 regoff)
+ {
+- u32 val;
+- u32 linktype = 0;
++ uint32_t val;
++ uint32_t val2;
++ uint32_t linktype = 0;
+
+ /* Check connect, init and coherency */
+ val = pci_read_config32(NODE_PCI(node, 0), regoff + 0x18);
+@@ -863,8 +864,13 @@ static u32 AMD_checkLinkType(u8 node, u8 link, u8 regoff)
+ if (linktype) {
+ /* Check gen3 */
+ val = pci_read_config32(NODE_PCI(node, 0), regoff + 0x08);
++ val = (val >> 8) & 0xf;
++ if (is_gt_rev_d()) {
++ val2 = pci_read_config32(NODE_PCI(node, 0), regoff + 0x1c);
++ val |= (val2 & 0x1) << 4;
++ }
+
+- if (((val >> 8) & 0x0F) > 6)
++ if (val > 6)
+ linktype |= HTPHY_LINKTYPE_HT3;
+ else
+ linktype |= HTPHY_LINKTYPE_HT1;
+@@ -1147,6 +1153,39 @@ static void cpuSetAMDPCI(u8 node)
+ pci_write_config32(NODE_PCI(node, 3), 0xd4, dword);
+ }
+
++ if (revision & AMD_FAM15_ALL) {
++ uint32_t f5x80;
++ uint8_t cu_enabled;
++ uint8_t compute_unit_count = 0;
++ uint8_t compute_unit_buffer_count;
++
++ /* Determine the number of active compute units on this node */
++ f5x80 = pci_read_config32(NODE_PCI(node, 5), 0x80);
++ cu_enabled = f5x80 & 0xf;
++ if (cu_enabled == 0x1)
++ compute_unit_count = 1;
++ if (cu_enabled == 0x3)
++ compute_unit_count = 2;
++ if (cu_enabled == 0x7)
++ compute_unit_count = 3;
++ if (cu_enabled == 0xf)
++ compute_unit_count = 4;
++
++ if (compute_unit_count == 1)
++ compute_unit_buffer_count = 0x1c;
++ else if (compute_unit_count == 2)
++ compute_unit_buffer_count = 0x18;
++ else if (compute_unit_count == 3)
++ compute_unit_buffer_count = 0x14;
++ else
++ compute_unit_buffer_count = 0x10;
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x1a0);
++ dword &= ~(0x1f << 4); /* L3FreeListCBC = compute_unit_buffer_count */
++ dword |= (compute_unit_buffer_count << 4);
++ pci_write_config32(NODE_PCI(node, 3), 0x1a0, dword);
++ }
++
+ printk(BIOS_DEBUG, " done\n");
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0098-amd-amdmct-mct_ddr3-Fix-poor-performance-on-Family-1.patch b/resources/libreboot/patch/kgpe-d16/0098-amd-amdmct-mct_ddr3-Fix-poor-performance-on-Family-1.patch
new file mode 100644
index 00000000..11d54711
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0098-amd-amdmct-mct_ddr3-Fix-poor-performance-on-Family-1.patch
@@ -0,0 +1,1087 @@
+From 71ffed0eabd830bba3170a8d9a8bf9f735306a34 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 19:05:29 -0500
+Subject: [PATCH 098/146] amd/amdmct/mct_ddr3: Fix poor performance on Family
+ 15h CPUs
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 370 ++++++++++++++++++++----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 1 +
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 65 ++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 49 +++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 195 ++++++++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 4 +
+ 6 files changed, 604 insertions(+), 80 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 4d8f3d0..539d0e8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -36,6 +36,8 @@
+ * supported.
+ */
+
++// #define DEBUG_DIMM_SPD 1
++
+ static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+@@ -172,7 +174,8 @@ static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel);
+ static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dimm);
+-static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2);
++static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat,
++ uint8_t dct, uint32_t misc2, uint32_t DramControl);
+ static void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat);
+ static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA, uint8_t Pass);
+@@ -1360,6 +1363,8 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ static void set_2t_configuration(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ uint32_t dev;
+ uint32_t reg;
+ uint32_t dword;
+@@ -1382,6 +1387,8 @@ static void set_2t_configuration(struct MCTStatStruc *pMCTstat,
+ else
+ dword &= ~(0x1 << 20); /* Clear 2T CMD mode */
+ Set_NB32_DCT(dev, dct, reg, dword);
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static void precise_ndelay_fam15(struct MCTStatStruc *pMCTstat, uint32_t nanoseconds) {
+@@ -1996,6 +2003,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ /* Disable training mode */
+ uint8_t lane;
+ uint8_t dimm;
++ uint16_t sword;
+ uint8_t receiver;
+ uint8_t max_lane;
+ uint8_t ecc_enabled;
+@@ -2010,21 +2018,37 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ uint16_t twrwrdd;
+ uint16_t cdd_twrwrdd;
+ uint16_t twrrd;
++ uint16_t cdd_twrrd;
++ uint16_t cdd_trwtto;
+ uint16_t trwtto;
+ uint8_t first_dimm;
+ uint16_t delay;
+ uint16_t delay2;
++ uint8_t min_value;
++ uint8_t write_early;
+ uint8_t read_odt_delay;
+ uint8_t write_odt_delay;
++ uint8_t buffer_data_delay;
++ int16_t latency_difference;
+ uint16_t difference;
+ uint16_t current_total_delay_1[MAX_BYTE_LANES];
+ uint16_t current_total_delay_2[MAX_BYTE_LANES];
++ uint8_t ddr_voltage_index;
++ uint8_t max_dimms_installable;
+
+ /* FIXME
+ * This should be platform configurable
+ */
+ uint8_t dimm_event_l_pin_support = 0;
+
++ if (pDCTstat->DIMMValidDCT[dct] == 0)
++ ddr_voltage_index = 1;
++ else
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++
++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
++ max_dimms_installable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
++
+ ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
+ if (ecc_enabled)
+ max_lane = 9;
+@@ -2058,6 +2082,24 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ else
+ write_odt_delay = 0;
+
++ dword = (Get_NB32_DCT(dev, dct, 0xa8) >> 24) & 0x3;
++ write_early = dword / 2;
++
++ latency_difference = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;
++ dword = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f;
++ latency_difference -= dword;
++
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++
++ /* TODO
++ * Implement LRDIMM support
++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.5
++ */
++ } else {
++ buffer_data_delay = 0;
++ }
++
+ /* TODO:
+ * Adjust trdrdsddc if four-rank DIMMs are installed per
+ * section 2.10.5.5.1 of the Family 15h BKDG.
+@@ -2093,7 +2135,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ }
+
+ /* Convert the difference to MEMCLKs */
+- cdd_trdrddd = (((cdd_trdrddd >> 5) & 0x1f) + 1) / 2;
++ cdd_trdrddd = (((cdd_trdrddd + (1 << 6) - 1) >> 6) & 0xf);
+
+ /* Calculate Trdrddd */
+ delay = (read_odt_delay + 3) * 2;
+@@ -2140,7 +2182,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ }
+
+ /* Convert the difference to MEMCLKs */
+- cdd_twrwrdd = (((cdd_twrwrdd >> 5) & 0x1f) + 1) / 2;
++ cdd_twrwrdd = (((cdd_twrwrdd + (1 << 6) - 1) >> 6) & 0xf);
+
+ /* Calculate Twrwrdd */
+ delay = (write_odt_delay + 3) * 2;
+@@ -2159,6 +2201,107 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword &= ~(0x1 << 18); /* DisAutoRefresh = 0 */
+ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */
+
++ /* Configure power saving options */
++ dword = Get_NB32_DCT(dev, dct, 0xa8); /* Dram Miscellaneous 2 */
++ dword |= (0x1 << 22); /* PrtlChPDEnhEn = 0x1 */
++ dword |= (0x1 << 21); /* AggrPDEn = 0x1 */
++ Set_NB32_DCT(dev, dct, 0xa8, dword); /* Dram Miscellaneous 2 */
++
++ /* Configure partial power down delay */
++ dword = Get_NB32(dev, 0x244); /* DRAM Controller Miscellaneous 3 */
++ dword &= ~0xf; /* PrtlChPDDynDly = 0x2 */
++ dword |= 0x2;
++ Set_NB32(dev, 0x244, dword); /* DRAM Controller Miscellaneous 3 */
++
++ /* Configure power save delays */
++ delay = 0xa;
++ delay2 = 0x3;
++
++ /* Family 15h BKDG Table 214 */
++ if ((pDCTstat->Status & (1 << SB_Registered))
++ || (pDCTstat->Status & (1 << SB_LoadReduced))) {
++ if (memclk_index <= 0x6) {
++ if (ddr_voltage_index < 0x4)
++ /* 1.5 or 1.35V */
++ delay2 = 0x3;
++ else
++ /* 1.25V */
++ delay2 = 0x4;
++ }
++ else if ((memclk_index == 0xa)
++ || (memclk_index == 0xe))
++ delay2 = 0x4;
++ else if (memclk_index == 0x12)
++ delay2 = 0x5;
++ else if (memclk_index == 0x16)
++ delay2 = 0x6;
++ } else {
++ if (memclk_index <= 0x6)
++ delay2 = 0x3;
++ else if ((memclk_index == 0xa)
++ || (memclk_index == 0xe))
++ delay2 = 0x4;
++ else if (memclk_index == 0x12)
++ delay2 = 0x5;
++ else if (memclk_index == 0x16)
++ delay2 = 0x6;
++ }
++
++ /* Family 15h BKDG Table 215 */
++ if (memclk_index <= 0x6)
++ delay = 0xa;
++ else if (memclk_index == 0xa)
++ delay = 0xd;
++ else if (memclk_index == 0xe)
++ delay = 0x10;
++ else if (memclk_index == 0x12)
++ delay = 0x14;
++ else if (memclk_index == 0x16)
++ delay = 0x17;
++
++ dword = Get_NB32_DCT(dev, dct, 0x248); /* Dram Power Management 0 */
++ dword &= ~(0x3f << 24); /* AggrPDDelay = 0x0 */
++ dword &= ~(0x3f << 16); /* PchgPDEnDelay = 0x1 */
++ dword |= (0x1 << 16);
++ dword &= ~(0x1f << 8); /* Txpdll = delay */
++ dword |= ((delay & 0x1f) << 8);
++ dword &= ~0xf; /* Txp = delay2 */
++ dword |= delay2 & 0xf;
++ Set_NB32_DCT(dev, dct, 0x248, dword); /* Dram Power Management 0 */
++
++ /* Family 15h BKDG Table 216 */
++ if (memclk_index <= 0x6) {
++ delay = 0x5;
++ delay2 = 0x3;
++ }
++ else if (memclk_index == 0xa) {
++ delay = 0x6;
++ delay2 = 0x3;
++ }
++ else if (memclk_index == 0xe) {
++ delay = 0x7;
++ delay2 = 0x4;
++ }
++ else if (memclk_index == 0x12) {
++ delay = 0x8;
++ delay2 = 0x4;
++ }
++ else if (memclk_index == 0x16) {
++ delay = 0xa;
++ delay2 = 0x5;
++ }
++
++ dword = Get_NB32_DCT(dev, dct, 0x24c); /* Dram Power Management 1 */
++ dword &= ~(0x3f << 24); /* Tcksrx = delay */
++ dword |= ((delay & 0x3f) << 24);
++ dword &= ~(0x3f << 16); /* Tcksre = delay */
++ dword |= ((delay & 0x3f) << 16);
++ dword &= ~(0x3f << 8); /* Tckesr = delay2 + 1 */
++ dword |= (((delay2 + 1) & 0x3f) << 8);
++ dword &= ~0xf; /* Tpd = delay2 */
++ dword |= delay2 & 0xf;
++ Set_NB32_DCT(dev, dct, 0x24c, dword); /* Dram Power Management 1 */
++
+ dword = Get_NB32_DCT(dev, dct, 0x94); /* DRAM Configuration High */
+ dword |= (0xf << 24); /* DcqBypassMax = 0xf */
+ dword |= (0x1 << 22); /* BankSwizzleMode = 1 */
+@@ -2211,15 +2354,98 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
+- /* TODO
+- * Calculate Twrrd per section 2.10.5.5.3 of the Family 15h BKDG
+- */
+- twrrd = 0xb;
++ /* Calculate the Critical Delay Difference for Twrrd */
++ cdd_twrrd = 0;
++ for (receiver = 0; receiver < 8; receiver += 2) {
++ dimm = (receiver >> 1);
+
+- /* TODO
+- * Calculate TrwtTO per section 2.10.5.5.4 of the Family 15h BKDG
+- */
+- trwtto = 0x16;
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
++ continue;
++
++ read_dqs_write_timing_control_registers(current_total_delay_1, dev, dct, dimm, index_reg);
++ read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
++
++ for (lane = 0; lane < max_lane; lane++) {
++ if (current_total_delay_1[lane] > current_total_delay_2[lane])
++ difference = current_total_delay_1[lane] - current_total_delay_2[lane];
++ else
++ difference = current_total_delay_2[lane] - current_total_delay_1[lane];
++
++ if (difference > cdd_twrrd)
++ cdd_twrrd = difference;
++ }
++ }
++
++ /* Convert the difference to MEMCLKs */
++ cdd_twrrd = (((cdd_twrrd + (1 << 6) - 1) >> 6) & 0xf);
++
++ /* Fam15h BKDG section 2.10.5.5.3 */
++ if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++
++ /* TODO
++ * Implement LRDIMM support
++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.5
++ */
++ twrrd = 0xb;
++ } else {
++ sword = (((int16_t)cdd_twrrd + 1 - ((int16_t)write_early * 2)) + 1) / 2;
++ if (sword < 0)
++ sword = 0;
++ if (((uint16_t)sword) > write_odt_delay)
++ dword = sword;
++ else
++ dword = write_odt_delay;
++ dword += 3;
++ if (latency_difference < dword) {
++ dword -= latency_difference;
++ if (dword < 1)
++ twrrd = 1;
++ else
++ twrrd = dword;
++ } else {
++ twrrd = 1;
++ }
++ }
++
++ /* Calculate the Critical Delay Difference for TrwtTO */
++ cdd_trwtto = 0;
++ for (receiver = 0; receiver < 8; receiver += 2) {
++ dimm = (receiver >> 1);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
++ continue;
++
++ read_dqs_receiver_enable_control_registers(current_total_delay_1, dev, dct, dimm, index_reg);
++ read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
++
++ for (lane = 0; lane < max_lane; lane++) {
++ if (current_total_delay_1[lane] > current_total_delay_2[lane])
++ difference = current_total_delay_1[lane] - current_total_delay_2[lane];
++ else
++ difference = current_total_delay_2[lane] - current_total_delay_1[lane];
++
++ if (difference > cdd_trwtto)
++ cdd_trwtto = difference;
++ }
++ }
++
++ /* Convert the difference to MEMCLKs */
++ cdd_trwtto = (((cdd_trwtto + (1 << 6) - 1) >> 6) & 0xf);
++
++ /* Fam15h BKDG section 2.10.5.5.4 */
++ if (max_dimms_installable == 1)
++ min_value = 0;
++ else
++ min_value = read_odt_delay + buffer_data_delay;
++ sword = (((int16_t)cdd_trwtto - 1 + ((int16_t)write_early * 2)) + 1) / 2;
++ sword += latency_difference + 3;
++ if (sword < 0)
++ sword = 0;
++ if (((uint16_t)sword) > min_value)
++ trwtto = (uint16_t)sword;
++ else
++ trwtto = min_value;
+
+ dword = Get_NB32_DCT(dev, dct, 0xa4); /* DRAM Controller Temperature Throttle */
+ dword &= ~(0x1 << 11); /* BwCapEn = 0 */
+@@ -2230,6 +2456,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword = Get_NB32_DCT(dev, dct, 0x110); /* DRAM Controller Select Low */
+ dword &= ~(0x1 << 2); /* DctSelIntLvEn = interleave_channels */
+ dword |= (interleave_channels & 0x1) << 2;
++ dword |= (0x3 << 6); /* DctSelIntLvAddr = 0x3 */
+ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */
+
+ /* NOTE
+@@ -2237,22 +2464,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ * otherwise semi-random lockups will occur due to misconfigured scrubbing hardware!
+ */
+
+- /* FIXME
+- * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
+- * Investigate and fix...
+- */
+-#if 0
+- /* Fam15h BKDG section 2.10.5.5.1 */
+- dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
+- dword &= ~(0xf << 24); /* TrdrdSdSc = 0x1 */
+- dword |= (0x1 << 24);
+- dword &= ~(0xf << 16); /* TrdrdSdDc = trdrdsddc */
+- dword |= ((trdrdsddc & 0xf) << 16);
+- dword &= ~(0xf); /* TrdrdDd = trdrddd */
+- dword |= (trdrddd & 0xf);
+- Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
+-#endif
+-
+ /* Fam15h BKDG section 2.10.5.5.2 */
+ dword = Get_NB32_DCT(dev, dct, 0x214); /* DRAM Timing 4 */
+ dword &= ~(0xf << 16); /* TwrwrSdSc = 0x1 */
+@@ -2265,8 +2476,14 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+
+ /* Fam15h BKDG section 2.10.5.5.3 */
+ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */
++ dword &= ~(0xf << 24); /* TrdrdSdSc = 0x1 */
++ dword |= (0x1 << 24);
++ dword &= ~(0xf << 16); /* TrdrdSdDc = trdrdsddc */
++ dword |= ((trdrdsddc & 0xf) << 16);
+ dword &= ~(0xf << 8); /* Twrrd = twrrd */
+ dword |= ((twrrd & 0xf) << 8);
++ dword &= ~(0xf); /* TrdrdDd = trdrddd */
++ dword |= (trdrddd & 0xf);
+ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */
+
+ /* Fam15h BKDG section 2.10.5.5.4 */
+@@ -2277,12 +2494,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
+ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */
+
+- /* Configure partial power down delay */
+- dword = Get_NB32(dev, 0x244); /* DRAM Controller Miscellaneous 3 */
+- dword &= ~0xf; /* PrtlChPDDynDly = 0x2 */
+- dword |= 0x2;
+- Set_NB32(dev, 0x244, dword); /* DRAM Controller Miscellaneous 3 */
+-
+ /* Enable prefetchers */
+ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
+ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
+@@ -2371,6 +2582,8 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+
+ mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
+
++ TrainMaxRdLatency_En_D(pMCTstat, pDCTstatA);
++
+ if (is_fam15h())
+ exit_training_mode_fam15(pMCTstat, pDCTstatA);
+ else
+@@ -2944,6 +3157,13 @@ static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
+ }
+
+ while(reg < reg_end) {
++ if ((reg & 0xFF) == 0x84) {
++ if (is_fam15h()) {
++ val = Get_NB32_DCT(dev, dct, reg);
++ val &= ~(0x1 << 23); /* Clear PchgPDModeSel */
++ val &= ~0x3; /* Clear BurstCtrl */
++ }
++ }
+ if ((reg & 0xFF) == 0x90) {
+ if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+ val = Get_NB32_DCT(dev, dct, reg); /* get DRAMConfigLow */
+@@ -3062,14 +3282,30 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+
+ /* Convert DRAM CycleTiming values and store into DCT structure */
+ byte = pDCTstat->DIMMAutoSpeed;
+- if (byte == 7)
+- tCK16x = 20;
+- else if (byte == 6)
+- tCK16x = 24;
+- else if (byte == 5)
+- tCK16x = 30;
+- else
+- tCK16x = 40;
++ if (is_fam15h()) {
++ if (byte == 0x16)
++ tCK16x = 17;
++ else if (byte == 0x12)
++ tCK16x = 20;
++ else if (byte == 0xe)
++ tCK16x = 24;
++ else if (byte == 0xa)
++ tCK16x = 30;
++ else if (byte == 0x6)
++ tCK16x = 40;
++ else
++ tCK16x = 48;
++ }
++ else {
++ if (byte == 7)
++ tCK16x = 20;
++ else if (byte == 6)
++ tCK16x = 24;
++ else if (byte == 5)
++ tCK16x = 30;
++ else
++ tCK16x = 40;
++ }
+
+ /* Notes:
+ 1. All secondary time values given in SPDs are in binary with units of ns.
+@@ -3102,7 +3338,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ val = Max_TrpT;
+ pDCTstat->Trp = val;
+
+- /*Trrd*/
++ /* Trrd */
+ pDCTstat->DIMMTrrd = Trrd;
+ val = Trrd / tCK16x;
+ if (Trrd % tCK16x) { /* round up number of busclocks */
+@@ -3220,21 +3456,31 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x200); /* DRAM Timing 0 */
+ dword &= ~(0x3f1f1f1f);
+- dword |= ((pDCTstat->Tras + 0xf) & 0x3f) << 24; /* Tras */
+- dword |= ((pDCTstat->Trp + 0x5) & 0x1f) << 16; /* Trp */
+- dword |= ((pDCTstat->Trcd + 0x5) & 0x1f) << 8; /* Trcd */
++ dword |= (pDCTstat->Tras & 0x3f) << 24; /* Tras */
++ val = pDCTstat->Trp;
++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
++ dword |= (val & 0x1f) << 16; /* Trp */
++ dword |= (pDCTstat->Trcd & 0x1f) << 8; /* Trcd */
+ dword |= (pDCTstat->CASL & 0x1f); /* Tcl */
+ Set_NB32_DCT(dev, dct, 0x200, dword); /* DRAM Timing 0 */
+
+ dword = Get_NB32_DCT(dev, dct, 0x204); /* DRAM Timing 1 */
+ dword &= ~(0x0f3f0f3f);
+- dword |= ((pDCTstat->Trtp + 0x4) & 0xf) << 24; /* Trtp */
+- if (pDCTstat->Tfaw != 0)
+- dword |= ((((pDCTstat->Tfaw - 0x1) * 2) + 0x10) & 0x3f) << 16; /* FourActWindow */
+- dword |= ((pDCTstat->Trrd + 0x4) & 0xf) << 8; /* Trrd */
+- dword |= ((pDCTstat->Trc + 0xb) & 0x3f); /* Trc */
++ dword |= (pDCTstat->Trtp & 0xf) << 24; /* Trtp */
++ if (pDCTstat->Tfaw != 0) {
++ val = pDCTstat->Tfaw;
++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
++ if ((val > 0x5) && (val < 0x2b))
++ dword |= (val & 0x3f) << 16; /* FourActWindow */
++ }
++ dword |= (pDCTstat->Trrd & 0xf) << 8; /* Trrd */
++ dword |= (pDCTstat->Trc & 0x3f); /* Trc */
+ Set_NB32_DCT(dev, dct, 0x204, dword); /* DRAM Timing 1 */
+
++ /* Trfc0-Trfc3 */
++ for (i=0; i<4; i++)
++ if (pDCTstat->Trfc[i] == 0x0)
++ pDCTstat->Trfc[i] = 0x4;
+ dword = Get_NB32_DCT(dev, dct, 0x208); /* DRAM Timing 2 */
+ dword &= ~(0x07070707);
+ dword |= (pDCTstat->Trfc[3] & 0x7) << 24; /* Trfc3 */
+@@ -3245,14 +3491,14 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x20c); /* DRAM Timing 3 */
+ dword &= ~(0x00000f00);
+- dword |= ((pDCTstat->Twtr + 0x4) & 0xf) << 8; /* Twtr */
++ dword |= (pDCTstat->Twtr & 0xf) << 8; /* Twtr */
+ dword &= ~(0x0000001f);
+ dword |= (Tcwl & 0x1f); /* Tcwl */
+ Set_NB32_DCT(dev, dct, 0x20c, dword); /* DRAM Timing 3 */
+
+ dword = Get_NB32_DCT(dev, dct, 0x22c); /* DRAM Timing 10 */
+ dword &= ~(0x0000001f);
+- dword |= ((pDCTstat->Twr + 0x4) & 0x1f); /* Twr */
++ dword |= (pDCTstat->Twr & 0x1f); /* Twr */
+ Set_NB32_DCT(dev, dct, 0x22c, dword); /* DRAM Timing 10 */
+
+ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+@@ -3848,6 +4094,8 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++ DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2, DramControl);
++
+ printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %08x\n", DramControl);
+ printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %08x\n", DramTimingLo);
+ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %08x\n", DramConfigMisc);
+@@ -3859,7 +4107,6 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ Set_NB32_DCT(dev, dct, 0x78, DramControl);
+ Set_NB32_DCT(dev, dct, 0x88, DramTimingLo);
+ Set_NB32_DCT(dev, dct, 0xa0, DramConfigMisc);
+- DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2);
+ Set_NB32_DCT(dev, dct, 0xa8, DramConfigMisc2);
+ Set_NB32_DCT(dev, dct, 0x90, DramConfigLo);
+ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+@@ -5226,6 +5473,16 @@ static void mct_PhyController_Config(struct MCTStatStruc *pMCTstat,
+ u32 dev = pDCTstat->dev_dct;
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3 | AMD_FAM15_ALL)) {
++ if (is_fam15h()) {
++ /* Set F2x[1, 0]98_x0D0F0F13 DllDisEarlyU and DllDisEarlyL to save power */
++ for (index = 0; index < 0x9; index++) {
++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8));
++ dword |= (0x1 << 1); /* DllDisEarlyU = 1 */
++ dword |= 0x1; /* DllDisEarlyL = 1 */
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8), dword);
++ }
++ }
++
+ if (pDCTstat->Dimmx4Present == 0) {
+ /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for
+ * additional power saving when x4 DIMMs are not present.
+@@ -5270,8 +5527,9 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ mct_ExtMCTConfig_Dx(pDCTstat);
+ } else {
+ /* Family 15h CPUs */
+- val = 0x0ce00f00 | 0x1 << 29; /* FlushWrOnStpGnt */
+- val |= 0x10 << 2; /* MctWrLimit = 16 */
++ val = 0x0ce00f00; /* FlushWrOnStpGnt = 0x0 */
++ val |= 0x10 << 2; /* MctWrLimit = 0x10 */
++ val |= 0x1; /* DctWrLimit = 0x1 */
+ Set_NB32(pDCTstat->dev_dct, 0x11c, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x1b0);
+@@ -6511,8 +6769,8 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
+ if (is_fam15h()) {
+- dword |= DramMRS;
+ dword &= ~0x00800003;
++ dword |= DramMRS;
+ } else {
+ dword &= ~0x00fc2f8f;
+ dword |= DramMRS;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index 486b16c..ec5658e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -988,6 +988,7 @@ void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat
+ uint64_t mctGetLogicalCPUID(u32 Node);
+ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u8 Pass);
++void TrainMaxRdLatency_En_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+ void TrainMaxReadLatency_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index bc9ac4b..eedff67 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -24,6 +24,9 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_
+ static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay,
+ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+
++static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat);
++
+ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u16 like,
+ u8 scale, u8 ChipSel);
+@@ -218,6 +221,27 @@ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+
++void TrainMaxRdLatency_En_D(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstatA)
++{
++ uint8_t node;
++ struct DCTStatStruc *pDCTstat;
++
++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
++ pDCTstat = pDCTstatA + node;
++
++ if (pDCTstat->DCTSysLimit) {
++ if (is_fam15h()) {
++ dqsTrainMaxRdLatency_SW_Fam15(pMCTstat, pDCTstat);
++ } else {
++ /* FIXME
++ * Implement Family 10h MaxRdLatency training
++ */
++ }
++ }
++ }
++}
++
+ static void SetEccDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+ {
+@@ -898,7 +922,7 @@ static void TrainDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
+ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.5
+ */
+ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, uint8_t dct)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t calc_min)
+ {
+ uint8_t dimm;
+ uint8_t lane;
+@@ -942,7 +966,8 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ p += (9 - dword);
+
+ /* 2.10.5.8.5 (4) */
+- p += 5;
++ if (!calc_min)
++ p += 5;
+
+ /* 2.10.5.8.5 (5) */
+ dword = Get_NB32_DCT(dev, dct, 0xa8);
+@@ -969,7 +994,8 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ p += (max_delay >> 5);
+
+ /* 2.10.5.8.5 (8) */
+- p += 5;
++ if (!calc_min)
++ p += 5;
+
+ /* 2.10.5.8.5 (9) */
+ t += 800;
+@@ -980,13 +1006,16 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
+
+ /* 2.10.5.8.5 (11) */
+- n -= 1;
++ if (!calc_min)
++ n -= 1;
+
+ /* 2.10.5.8.5 (12) */
+- dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210);
+- dword &= ~(0x3ff << 22);
+- dword |= (((n - 1) & 0x3ff) << 22);
+- Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
++ if (!calc_min) {
++ dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210);
++ dword &= ~(0x3ff << 22);
++ dword |= (((n - 1) & 0x3ff) << 22);
++ Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
++ }
+
+ /* Save result for later use */
+ pDCTstat->CH_MaxRdLat[dct] = n - 1;
+@@ -1107,6 +1136,9 @@ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ } else if (lane < 8) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ } else if (lane == 0xff) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0xffffffff);
++ Set_NB32_DCT(dev, dct, 0x278, ~0xffffffff);
+ } else {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+@@ -1114,8 +1146,9 @@ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x27c);
+ dword &= ~(0xff); /* EccMask = 0 */
+- if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+- dword |= 0xff; /* EccMask = 0xff */
++ if (lane != 0xff)
++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
++ dword |= 0xff; /* EccMask = 0xff */
+ Set_NB32_DCT(dev, dct, 0x27c, dword);
+
+ dword = Get_NB32_DCT(dev, dct, 0x270);
+@@ -1184,6 +1217,9 @@ static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ } else if (lane < 8) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ } else if (lane == 0xff) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0xffffffff);
++ Set_NB32_DCT(dev, dct, 0x278, ~0xffffffff);
+ } else {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+@@ -1191,8 +1227,9 @@ static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x27c);
+ dword &= ~(0xff); /* EccMask = 0 */
+- if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+- dword |= 0xff; /* EccMask = 0xff */
++ if (lane != 0xff)
++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
++ dword |= 0xff; /* EccMask = 0xff */
+ Set_NB32_DCT(dev, dct, 0x27c, dword);
+
+ dword = Get_NB32_DCT(dev, dct, 0x270);
+@@ -1278,7 +1315,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint32_t dev = pDCTstat->dev_dct;
+
+ /* Calculate and program MaxRdLatency */
+- Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct, 0);
+
+ Errors = 0;
+ dual_rank = 0;
+@@ -1636,7 +1673,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
+ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+
+ /* Calculate and program MaxRdLatency */
+- Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct, 0);
+
+ /* 2.10.5.8.3 (4 B) */
+ dqs_results_array[current_phy_phase_delay[lane]] = TrainDQSRdWrPos_D_Fam15(pMCTstat, pDCTstat, dct, Receiver, Receiver + 2, lane, lane + 1);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index 738304e..3da28b3 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -19,7 +19,8 @@
+ */
+
+ /* mct_SetDramConfigMisc2_Cx & mct_SetDramConfigMisc2_Dx */
+-u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
++u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat,
++ uint8_t dct, uint32_t misc2, uint32_t DramControl)
+ {
+ u32 val;
+
+@@ -28,17 +29,47 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
+ uint8_t cs_mux_45;
+ uint8_t cs_mux_67;
++ uint32_t f2x80;
+
+- /* BKDG v3.14 Table 200 / Table 201 */
+- if (MaxDimmsInstallable < 3) {
+- cs_mux_45 = 1;
+- cs_mux_67 = 1;
+- } else {
++ misc2 &= ~(0x1 << 28); /* FastSelfRefEntryDis = 0x0 */
++ if (MaxDimmsInstallable == 3) {
++ /* FIXME 3 DIMMS per channel unimplemented */
+ cs_mux_45 = 0;
++ } else {
++ uint32_t f2x60 = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x60);
++ f2x80 = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x80);
++ if ((((f2x80 & 0xf) == 0x7) || ((f2x80 & 0xf) == 0x9))
++ && ((f2x60 & 0x3) == 0x3))
++ cs_mux_45 = 1;
++ else if ((((f2x80 & 0xa) == 0x7) || ((f2x80 & 0xb) == 0x9))
++ && ((f2x60 & 0x3) > 0x1))
++ cs_mux_45 = 1;
++ else
++ cs_mux_45 = 0;
++ }
++
++ if (MaxDimmsInstallable == 1) {
++ cs_mux_67 = 0;
++ } else if (MaxDimmsInstallable == 2) {
++ uint32_t f2x64 = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x64);
++ f2x80 = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x80);
++ if (((((f2x80 >> 4) & 0xf) == 0x7) || (((f2x80 >> 4) & 0xf) == 0x9))
++ && ((f2x64 & 0x3) == 0x3))
++ cs_mux_67 = 1;
++ else if (((((f2x80 >> 4) & 0xa) == 0x7) || (((f2x80 >> 4) & 0xb) == 0x9))
++ && ((f2x64 & 0x3) > 0x1))
++ cs_mux_67 = 1;
++ else
++ cs_mux_67 = 0;
++ } else {
++ /* FIXME 3 DIMMS per channel unimplemented */
+ cs_mux_67 = 0;
+ }
+- misc2 |= (cs_mux_45 & 0x1) << 26;
+- misc2 |= (cs_mux_67 & 0x1) << 27;
++
++ misc2 &= ~(0x1 << 27); /* CsMux67 = cs_mux_67 */
++ misc2 |= ((cs_mux_67 & 0x1) << 27);
++ misc2 &= ~(0x1 << 26); /* CsMux45 = cs_mux_45 */
++ misc2 |= ((cs_mux_45 & 0x1) << 26);
+ } else if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ misc2 |= 1 << SubMemclkRegDly;
+@@ -50,8 +81,8 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+
+ if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+ misc2 |= 1 << OdtSwizzle;
+- val = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x78);
+
++ val = DramControl;
+ val &= 7;
+ val = ((~val) & 0xff) + 1;
+ val += 6;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index 3af3eb2..b71b327 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -1425,7 +1425,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ }
+
+ /* Calculate and program MaxRdLatency */
+- Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel);
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel, 0);
+
+ if(_DisableDramECC) {
+ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
+@@ -1488,6 +1488,199 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ printk(BIOS_DEBUG, "TrainRcvrEn: Done\n\n");
+ }
+
++static void write_max_read_latency_to_registers(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint16_t latency)
++{
++ uint32_t dword;
++ uint8_t nb_pstate;
++
++ for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) {
++ dword = Get_NB32_DCT_NBPstate(pDCTstat->dev_dct, dct, nb_pstate, 0x210);
++ dword &= ~(0x3ff << 22);
++ dword |= ((latency & 0x3ff) << 22);
++ Set_NB32_DCT_NBPstate(pDCTstat->dev_dct, dct, nb_pstate, 0x210, dword);
++ }
++}
++
++/* DQS MaxRdLatency Training (Family 15h)
++ * Algorithm detailed in:
++ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.5.1
++ * This algorithm runs at the highest supported MEMCLK.
++ */
++static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
++ struct DCTStatStruc *pDCTstat)
++{
++ u8 Channel;
++ u8 Addl_Index = 0;
++ u8 Receiver;
++ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
++ u32 Errors;
++
++ u32 dev;
++ u32 index_reg;
++ u32 ch_start, ch_end;
++ u32 msr;
++ u32 cr4;
++ u32 lo, hi;
++
++ uint32_t dword;
++ uint8_t dimm;
++ uint8_t lane;
++ uint8_t mem_clk;
++ uint32_t nb_clk;
++ uint8_t nb_pstate;
++ uint16_t current_total_delay[MAX_BYTE_LANES];
++ uint16_t current_rdqs_total_delay[MAX_BYTE_LANES];
++ uint8_t current_worst_case_total_delay_dimm;
++ uint16_t current_worst_case_total_delay_value;
++
++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
++
++ print_debug_dqs("\nTrainMaxRdLatency: Node", pDCTstat->Node_ID, 0);
++
++ dev = pDCTstat->dev_dct;
++ index_reg = 0x98;
++ ch_start = 0;
++ ch_end = 2;
++
++ cr4 = read_cr4();
++ if(cr4 & ( 1 << 9)) { /* save the old value */
++ _SSE2 = 1;
++ }
++ cr4 |= (1 << 9); /* OSFXSR enable SSE2 */
++ write_cr4(cr4);
++
++ msr = HWCR;
++ _RDMSR(msr, &lo, &hi);
++ /* FIXME: Why use SSEDIS */
++ if(lo & (1 << 17)) { /* save the old value */
++ _Wrap32Dis = 1;
++ }
++ lo |= (1 << 17); /* HWCR.wrap32dis */
++ lo &= ~(1 << 15); /* SSEDIS */
++ _WRMSR(msr, lo, hi); /* Setting wrap32dis allows 64-bit memory references in real mode */
++
++ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
++
++ Errors = 0;
++ dev = pDCTstat->dev_dct;
++
++ for (Channel = 0; Channel < 2; Channel++) {
++ print_debug_dqs("\tTrainMaxRdLatency51: Node ", pDCTstat->Node_ID, 1);
++ print_debug_dqs("\tTrainMaxRdLatency51: Channel ", Channel, 1);
++ pDCTstat->Channel = Channel;
++
++ if (pDCTstat->DIMMValidDCT[Channel] == 0)
++ continue;
++
++ mem_clk = Get_NB32_DCT(dev, Channel, 0x94) & 0x1f;
++
++ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
++
++ /* Find DIMM with worst case receiver enable delays */
++ current_worst_case_total_delay_dimm = 0;
++ current_worst_case_total_delay_value = 0;
++
++ /* There are four receiver pairs, loosely associated with chipselects.
++ * This is essentially looping over each DIMM.
++ */
++ for (; Receiver < 8; Receiver += 2) {
++ Addl_Index = (Receiver >> 1) * 3 + 0x10;
++ dimm = (Receiver >> 1);
++
++ print_debug_dqs("\t\tTrainMaxRdLatency52: index ", Addl_Index, 2);
++
++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
++ continue;
++ }
++
++ /* Retrieve the total delay values from pass 1 of DQS receiver enable training */
++ read_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
++ read_read_dqs_timing_control_registers(current_rdqs_total_delay, dev, Channel, dimm, index_reg);
++
++ for (lane = 0; lane < 8; lane++) {
++ current_total_delay[lane] += current_rdqs_total_delay[lane];
++ if (current_total_delay[lane] > current_worst_case_total_delay_value) {
++ current_worst_case_total_delay_dimm = dimm;
++ current_worst_case_total_delay_value = current_total_delay[lane];
++ }
++ }
++
++#if DQS_TRAIN_DEBUG > 0
++ for (lane = 0; lane < 8; lane++)
++ print_debug_dqs_pair("\t\tTrainMaxRdLatency56: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
++#endif
++ }
++
++ /* 2.10.5.8.5.1.1 */
++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel, 1);
++
++ /* 2.10.5.8.5.1.[2,3]
++ * Write the DRAM training pattern to the test address
++ */
++ write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff);
++
++ /* 2.10.5.8.5.1.4
++ * Incrementally test each MaxRdLatency candidate
++ */
++ for (; pDCTstat->CH_MaxRdLat[Channel] < 0x3ff; pDCTstat->CH_MaxRdLat[Channel]++) {
++ write_max_read_latency_to_registers(pMCTstat, pDCTstat, Channel, pDCTstat->CH_MaxRdLat[Channel]);
++ read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff);
++ dword = Get_NB32_DCT(dev, Channel, 0x268) & 0x3ffff;
++ if (!dword)
++ break;
++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000050, 0x13131313);
++ }
++
++ /* 2.10.5.8.5.1.5 */
++ nb_pstate = 0;
++ mem_clk = Get_NB32_DCT(dev, Channel, 0x94) & 0x1f;
++ if (fam15h_freq_tab[mem_clk] == 0) {
++ return;
++ }
++ dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4))); /* Retrieve NbDid, NbFid */
++ nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1);
++
++ pDCTstat->CH_MaxRdLat[Channel]++;
++ pDCTstat->CH_MaxRdLat[Channel] += ((((uint64_t)15 * 100000000000ULL) / ((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL))
++ * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
++
++ write_max_read_latency_to_registers(pMCTstat, pDCTstat, Channel, pDCTstat->CH_MaxRdLat[Channel]);
++ }
++
++ if(_DisableDramECC) {
++ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
++ }
++
++ if(!_Wrap32Dis) {
++ msr = HWCR;
++ _RDMSR(msr, &lo, &hi);
++ lo &= ~(1<<17); /* restore HWCR.wrap32dis */
++ _WRMSR(msr, lo, hi);
++ }
++ if(!_SSE2){
++ cr4 = read_cr4();
++ cr4 &= ~(1<<9); /* restore cr4.OSFXSR */
++ write_cr4(cr4);
++ }
++
++#if DQS_TRAIN_DEBUG > 0
++ {
++ u8 ChannelDTD;
++ printk(BIOS_DEBUG, "TrainMaxRdLatency: CH_MaxRdLat:\n");
++ for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
++ printk(BIOS_DEBUG, "Channel:%x: %x\n",
++ ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
++ }
++ }
++#endif
++
++ printk(BIOS_DEBUG, "TrainMaxRdLatency: Status %x\n", pDCTstat->Status);
++ printk(BIOS_DEBUG, "TrainMaxRdLatency: ErrStatus %x\n", pDCTstat->ErrStatus);
++ printk(BIOS_DEBUG, "TrainMaxRdLatency: ErrCode %x\n", pDCTstat->ErrCode);
++ printk(BIOS_DEBUG, "TrainMaxRdLatency: Done\n\n");
++}
++
+ u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+ if (pDCTstat->DIMMValidDCT[dct] == 0 ) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+index 3153e46..28cc8f6 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+@@ -172,6 +172,8 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
++
+ uint8_t DCT0Present;
+ uint8_t DCT1Present;
+ uint32_t dword;
+@@ -313,6 +315,8 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ mct_Wait(15000); /* Wait for 750us */
+ }
+ }
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ /*
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0099-northbridge-amd-amdht-Fix-poor-performance-on-Family.patch b/resources/libreboot/patch/kgpe-d16/0099-northbridge-amd-amdht-Fix-poor-performance-on-Family.patch
new file mode 100644
index 00000000..32121d3e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0099-northbridge-amd-amdht-Fix-poor-performance-on-Family.patch
@@ -0,0 +1,28 @@
+From e4453fd27b842d13276b0e043280031de7f52140 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 19:05:45 -0500
+Subject: [PATCH 099/146] northbridge/amd/amdht: Fix poor performance on
+ Family 15h CPUs
+
+---
+ src/northbridge/amd/amdht/h3ncmn.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index e03e5eb..e377ff2 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -1559,6 +1559,10 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ } else {
+ temp2 = 0x0;
+ }
++ /* NOTE
++ * The Family 15h BKDG Rev. 3.14 is wrong
++ * Freq[4] must be set before Freq[3:0], otherwise the register writes will be ignored!
++ */
+ if (is_gt_rev_d())
+ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG_2, 0, 0, &temp2);
+ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG, 11, 8, &temp);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0100-northbridge-amd-amdfam10-Fix-poor-performance-on-Fam.patch b/resources/libreboot/patch/kgpe-d16/0100-northbridge-amd-amdfam10-Fix-poor-performance-on-Fam.patch
new file mode 100644
index 00000000..014029dd
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0100-northbridge-amd-amdfam10-Fix-poor-performance-on-Fam.patch
@@ -0,0 +1,70 @@
+From 3a8253f5cd0093c33294f8c8399acaa60100b880 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 19:06:09 -0500
+Subject: [PATCH 100/146] northbridge/amd/amdfam10: Fix poor performance on
+ Family 15h CPUs
+
+---
+ src/northbridge/amd/amdfam10/nb_control.c | 4 ++--
+ src/northbridge/amd/amdfam10/northbridge.c | 21 +++++++++++++++++++++
+ 2 files changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/nb_control.c b/src/northbridge/amd/amdfam10/nb_control.c
+index f95b6f8..8e8dd57 100644
+--- a/src/northbridge/amd/amdfam10/nb_control.c
++++ b/src/northbridge/amd/amdfam10/nb_control.c
+@@ -60,10 +60,10 @@ static void nb_control_init(struct device *dev)
+ pci_write_config32(dev, 0xe0, dword);
+
+ /* Configure northbridge P-states */
+- dword = pci_read_config32(dev, 0xe0);
++ dword = pci_read_config32(dev, 0x170);
+ dword &= ~(0x7 << 9); /* NbPstateThreshold = compute_unit_count */
+ dword |= (compute_unit_count & 0x7) << 9;
+- pci_write_config32(dev, 0xe0, dword);
++ pci_write_config32(dev, 0x170, dword);
+
+ printk(BIOS_DEBUG, "done.\n");
+ }
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 2c8ee08..a60daf9 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -1755,6 +1755,8 @@ static void detect_and_enable_probe_filter(device_t dev)
+
+ disable_cache();
+ asm("wbinvd");
++
++ /* Enable probe filter */
+ for (i = 0; i < sysconf.nodes; i++) {
+ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
+
+@@ -1771,6 +1773,25 @@ static void detect_and_enable_probe_filter(device_t dev)
+ do {
+ } while (!(pci_read_config32(f3x_dev, 0x1d4) & (0x1 << 19)));
+ }
++
++ if (is_fam15h()) {
++ printk(BIOS_DEBUG, "Enabling ATM mode\n");
++
++ /* Enable ATM mode */
++ for (i = 0; i < sysconf.nodes; i++) {
++ device_t f0x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 0));
++ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
++
++ dword = pci_read_config32(f0x_dev, 0x68);
++ dword |= (0x1 << 12); /* ATMModeEn = 1 */
++ pci_write_config32(f0x_dev, 0x68, dword);
++
++ dword = pci_read_config32(f3x_dev, 0x1b8);
++ dword |= (0x1 << 27); /* L3ATMModeEn = 1 */
++ pci_write_config32(f3x_dev, 0x1b8, dword);
++ }
++ }
++
+ enable_cache();
+
+ /* Reenable L3 and DRAM scrubbers */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0101-cpu-amd-model_10xxx-Configure-NB-register-2.patch b/resources/libreboot/patch/kgpe-d16/0101-cpu-amd-model_10xxx-Configure-NB-register-2.patch
new file mode 100644
index 00000000..ba82f449
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0101-cpu-amd-model_10xxx-Configure-NB-register-2.patch
@@ -0,0 +1,31 @@
+From 29c6cf2af61d3e131fc1c8bbcebe9335bcd2e776 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 23:58:28 -0500
+Subject: [PATCH 101/146] cpu/amd/model_10xxx: Configure NB register 2
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 4868c5c..5ab4335 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -621,6 +621,14 @@ static const struct {
+ [5] DisPciCfgCpuMstAbtRsp = 1,
+ [1] SyncFloodOnUsPwDataErr = 1 */
+
++ /* NB Configuration 2 */
++ { 3, 0x188, AMD_DR_GT_B0, AMD_PTYPE_ALL,
++ 0x00000010, 0x00000010 }, /* EnStpGntOnFlushMaskWakeup = 0x1 */
++
++ /* NB Configuration 2 */
++ { 3, 0x188, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000200, 0x00000200 }, /* DisL3HiPriFreeListAlloc = 0x1 */
++
+ /* errata 346 - Fam10 C2, C3
+ * System software should set F3x188[22] to 1b. */
+ { 3, 0x188, AMD_DR_Cx, AMD_PTYPE_ALL,
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0102-cpu-amd-model_10xxx-Set-up-link-XCS-token-counts-on-.patch b/resources/libreboot/patch/kgpe-d16/0102-cpu-amd-model_10xxx-Set-up-link-XCS-token-counts-on-.patch
new file mode 100644
index 00000000..4b746b8a
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0102-cpu-amd-model_10xxx-Set-up-link-XCS-token-counts-on-.patch
@@ -0,0 +1,342 @@
+From 8cc7e05b370f7b24a694da4d30cfe719d45d6da9 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 23:59:17 -0500
+Subject: [PATCH 102/146] cpu/amd/model_10xxx: Set up link XCS token counts on
+ Family 15h
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 304 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 304 insertions(+)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 15ed259..7e39c6d 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1053,6 +1053,12 @@ static void cpuSetAMDPCI(u8 node)
+ uint32_t dword;
+ uint64_t revision;
+
++ /* FIXME
++ * This should be configurable
++ */
++ uint8_t sockets = 2;
++ uint8_t sockets_populated = 2;
++
+ printk(BIOS_DEBUG, "cpuSetAMDPCI %02d", node);
+
+ revision = mctGetLogicalCPUID(node);
+@@ -1158,6 +1164,15 @@ static void cpuSetAMDPCI(u8 node)
+ uint8_t cu_enabled;
+ uint8_t compute_unit_count = 0;
+ uint8_t compute_unit_buffer_count;
++
++ uint32_t f3xe8;
++ uint8_t dual_node = 0;
++
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
+
+ /* Determine the number of active compute units on this node */
+ f5x80 = pci_read_config32(NODE_PCI(node, 5), 0x80);
+@@ -1184,6 +1199,295 @@ static void cpuSetAMDPCI(u8 node)
+ dword &= ~(0x1f << 4); /* L3FreeListCBC = compute_unit_buffer_count */
+ dword |= (compute_unit_buffer_count << 4);
+ pci_write_config32(NODE_PCI(node, 3), 0x1a0, dword);
++
++ /* Set up the Link to XCS Token Counts */
++ uint8_t isoc_rsp_tok_1;
++ uint8_t isoc_preq_tok_1;
++ uint8_t isoc_req_tok_1;
++ uint8_t probe_tok_1;
++ uint8_t rsp_tok_1;
++ uint8_t preq_tok_1;
++ uint8_t req_tok_1;
++ uint8_t isoc_rsp_tok_0;
++ uint8_t isoc_preq_tok_0;
++ uint8_t isoc_req_tok_0;
++ uint8_t free_tokens;
++ uint8_t probe_tok_0;
++ uint8_t rsp_tok_0;
++ uint8_t preq_tok_0;
++ uint8_t req_tok_0;
++
++ uint8_t link;
++ uint8_t ganged;
++ uint8_t iolink;
++ uint8_t probe_filter_enabled = !!dual_node;
++ for (link = 0; link < 4; link++) {
++ if (AMD_CpuFindCapability(node, link, &offset)) {
++ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
++ iolink = (AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++
++ /* Set defaults */
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = !ganged;
++ rsp_tok_1 = !ganged;
++ preq_tok_1 = !ganged;
++ req_tok_1 = !ganged;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 0;
++ free_tokens = 0;
++ probe_tok_0 = ((ganged)?2:1);
++ rsp_tok_0 = ((ganged)?2:1);
++ preq_tok_0 = ((ganged)?2:1);
++ req_tok_0 = ((ganged)?2:1);
++
++ if (!iolink && ganged) {
++ if (!dual_node) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 3;
++ probe_tok_0 = 2;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ } else {
++ if ((sockets == 1)
++ || ((sockets == 2) && (sockets_populated == 1))) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 2;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ } else if (((sockets == 2) && (sockets_populated == 2))
++ || ((sockets == 4) && (sockets_populated == 2))) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ } else if ((sockets == 4) && (sockets_populated == 4)) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 2;
++ rsp_tok_0 = 1;
++ preq_tok_0 = 1;
++ req_tok_0 = 2;
++ }
++ }
++ } else if (!iolink && !ganged) {
++ if ((sockets == 1)
++ || ((sockets == 2) && (sockets_populated == 1))) {
++ if (probe_filter_enabled) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 1;
++ rsp_tok_1 = 1;
++ preq_tok_1 = 1;
++ req_tok_1 = 1;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 1;
++ req_tok_0 = 1;
++ } else {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 1;
++ rsp_tok_1 = 1;
++ preq_tok_1 = 1;
++ req_tok_1 = 1;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 1;
++ preq_tok_0 = 1;
++ req_tok_0 = 1;
++ }
++ } else if ((sockets == 2) && (sockets_populated == 2)) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 1;
++ probe_tok_1 = 1;
++ rsp_tok_1 = 1;
++ preq_tok_1 = 1;
++ req_tok_1 = 1;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 2;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 1;
++ preq_tok_0 = 1;
++ req_tok_0 = 1;
++ } else if ((sockets == 4) && (sockets_populated == 2)) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 1;
++ probe_tok_1 = 1;
++ rsp_tok_1 = 1;
++ preq_tok_1 = 1;
++ req_tok_1 = 1;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 4;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 1;
++ preq_tok_0 = 1;
++ req_tok_0 = 1;
++ } else if ((sockets == 4) && (sockets_populated == 4)) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 1;
++ probe_tok_1 = 1;
++ rsp_tok_1 = 1;
++ preq_tok_1 = 1;
++ req_tok_1 = 1;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 1;
++ rsp_tok_0 = 1;
++ preq_tok_0 = 1;
++ req_tok_0 = 1;
++ }
++ } else if (iolink && ganged) {
++ if (!dual_node) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 3;
++ probe_tok_0 = 0;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ } else if ((sockets == 1)
++ || (sockets == 2)
++ || ((sockets == 4) && (sockets_populated == 2))) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 1;
++ free_tokens = 0;
++ probe_tok_0 = 0;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ } else if ((sockets == 4) && (sockets_populated == 4)) {
++ isoc_rsp_tok_1 = 0;
++ isoc_preq_tok_1 = 0;
++ isoc_req_tok_1 = 0;
++ probe_tok_1 = 0;
++ rsp_tok_1 = 0;
++ preq_tok_1 = 0;
++ req_tok_1 = 0;
++ isoc_rsp_tok_0 = 0;
++ isoc_preq_tok_0 = 0;
++ isoc_req_tok_0 = 2;
++ free_tokens = 0;
++ probe_tok_0 = 2;
++ rsp_tok_0 = 2;
++ preq_tok_0 = 2;
++ req_tok_0 = 2;
++ }
++ }
++
++ dword = pci_read_config32(NODE_PCI(node, 3), (link << 2) + 0x148);
++ dword &= ~(0x3 << 30); /* FreeTok[3:2] = free_tokens[3:2] */
++ dword |= (((free_tokens >> 2) & 0x3) << 30);
++ dword &= ~(0x1 << 28); /* IsocRspTok1 = isoc_rsp_tok_1 */
++ dword |= (((isoc_rsp_tok_1) & 0x1) << 28);
++ dword &= ~(0x1 << 26); /* IsocPreqTok1 = isoc_preq_tok_1 */
++ dword |= (((isoc_preq_tok_1) & 0x1) << 26);
++ dword &= ~(0x1 << 24); /* IsocReqTok1 = isoc_req_tok_1 */
++ dword |= (((isoc_req_tok_1) & 0x1) << 24);
++ dword &= ~(0x3 << 22); /* ProbeTok1 = probe_tok_1 */
++ dword |= (((probe_tok_1) & 0x3) << 22);
++ dword &= ~(0x3 << 20); /* RspTok1 = rsp_tok_1 */
++ dword |= (((rsp_tok_1) & 0x3) << 20);
++ dword &= ~(0x3 << 18); /* PReqTok1 = preq_tok_1 */
++ dword |= (((preq_tok_1) & 0x3) << 18);
++ dword &= ~(0x3 << 16); /* ReqTok1 = req_tok_1 */
++ dword |= (((req_tok_1) & 0x3) << 16);
++ dword &= ~(0x3 << 14); /* FreeTok[1:0] = free_tokens[1:0] */
++ dword |= (((free_tokens) & 0x3) << 14);
++ dword &= ~(0x3 << 12); /* IsocRspTok0 = isoc_rsp_tok_0 */
++ dword |= (((isoc_rsp_tok_0) & 0x3) << 12);
++ dword &= ~(0x3 << 10); /* IsocPreqTok0 = isoc_preq_tok_0 */
++ dword |= (((isoc_preq_tok_0) & 0x3) << 10);
++ dword &= ~(0x3 << 8); /* IsocReqTok0 = isoc_req_tok_0 */
++ dword |= (((isoc_req_tok_0) & 0x3) << 8);
++ dword &= ~(0x3 << 6); /* ProbeTok0 = probe_tok_0 */
++ dword |= (((probe_tok_0) & 0x3) << 6);
++ dword &= ~(0x3 << 4); /* RspTok0 = rsp_tok_0 */
++ dword |= (((rsp_tok_0) & 0x3) << 4);
++ dword &= ~(0x3 << 2); /* PReqTok0 = preq_tok_0 */
++ dword |= (((preq_tok_0) & 0x3) << 2);
++ dword &= ~(0x3 << 0); /* ReqTok0 = req_tok_0 */
++ dword |= (((req_tok_0) & 0x3) << 0);
++ pci_write_config32(NODE_PCI(node, 3), (link << 2) + 0x148, dword);
++ }
++ }
+ }
+
+ printk(BIOS_DEBUG, " done\n");
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0103-northbridge-amd-amdmct-mct_ddr3-Force-retraining-on-.patch b/resources/libreboot/patch/kgpe-d16/0103-northbridge-amd-amdmct-mct_ddr3-Force-retraining-on-.patch
new file mode 100644
index 00000000..f7e950fa
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0103-northbridge-amd-amdmct-mct_ddr3-Force-retraining-on-.patch
@@ -0,0 +1,34 @@
+From 71edc36237fef86f4ee712b1d6edc0191b38714c Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 7 Aug 2015 23:59:33 -0500
+Subject: [PATCH 103/146] northbridge/amd/amdmct/mct_ddr3: Force retraining on
+ every boot
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 539d0e8..166cd3d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1729,6 +1729,16 @@ restartinit:
+ allow_config_restore = 0;
+ }
+
++ /* FIXME
++ * Stability issues have arisen on multiple Family 15h systems
++ * when configuration restoration is enabled. In all cases these
++ * stability issues resolved by allowing the RAM to go through a
++ * full training cycle.
++ *
++ * Debug and reenable this!
++ */
++ allow_config_restore = 0;
++
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0104-northbridge-amd-amdfam10-Fix-invalid-NUMA-table.patch b/resources/libreboot/patch/kgpe-d16/0104-northbridge-amd-amdfam10-Fix-invalid-NUMA-table.patch
new file mode 100644
index 00000000..b81057a9
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0104-northbridge-amd-amdfam10-Fix-invalid-NUMA-table.patch
@@ -0,0 +1,26 @@
+From 7998b24deb6711af2bea484fc010a163bfece9af Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 02:40:58 -0500
+Subject: [PATCH 104/146] northbridge/amd/amdfam10: Fix invalid NUMA table
+
+---
+ src/northbridge/amd/amdfam10/acpi.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/northbridge/amd/amdfam10/acpi.c b/src/northbridge/amd/amdfam10/acpi.c
+index 92433bb..23cf086 100644
+--- a/src/northbridge/amd/amdfam10/acpi.c
++++ b/src/northbridge/amd/amdfam10/acpi.c
+@@ -106,7 +106,8 @@ static void set_srat_mem(void *gp, struct device *dev, struct resource *res)
+ }
+
+ // need to figure out NV
+- state->current += acpi_create_srat_mem((acpi_srat_mem_t *)state->current, (res->index & 0xf), basek, sizek, 1);
++ if (res->index > 0xf)
++ state->current += acpi_create_srat_mem((acpi_srat_mem_t *)state->current, (res->index & 0xf), basek, sizek, 1);
+ }
+
+ static unsigned long acpi_fill_srat(unsigned long current)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0105-northbridge-amd-amdfam10-Add-Family-15h-cache-partit.patch b/resources/libreboot/patch/kgpe-d16/0105-northbridge-amd-amdfam10-Add-Family-15h-cache-partit.patch
new file mode 100644
index 00000000..e838e00f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0105-northbridge-amd-amdfam10-Add-Family-15h-cache-partit.patch
@@ -0,0 +1,121 @@
+From 6433fa7492f9fd6ff776af5e3db901fcc6f55136 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 20:29:27 -0500
+Subject: [PATCH 105/146] northbridge/amd/amdfam10: Add Family 15h cache
+ partitioning support
+
+---
+ src/northbridge/amd/amdfam10/northbridge.c | 94 ++++++++++++++++++++++++++++
+ 1 file changed, 94 insertions(+)
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index a60daf9..74b8709 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -1805,9 +1805,103 @@ static void detect_and_enable_probe_filter(device_t dev)
+ }
+ }
+
++static void detect_and_enable_cache_partitioning(device_t dev)
++{
++ uint8_t i;
++ uint32_t dword;
++
++ if (is_fam15h()) {
++ printk(BIOS_DEBUG, "Enabling L3 cache partitioning\n");
++
++ uint32_t f5x80;
++ uint8_t cu_enabled;
++ uint8_t compute_unit_count = 0;
++
++ uint32_t f3xe8;
++ uint8_t dual_node = 0;
++
++ for (i = 0; i < sysconf.nodes; i++) {
++ device_t f3x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 3));
++ device_t f4x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 4));
++ device_t f5x_dev = dev_find_slot(0, PCI_DEVFN(0x18 + i, 5));
++
++ f3xe8 = pci_read_config32(f3x_dev, 0xe8);
++
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
++
++ /* Determine the number of active compute units on this node */
++ f5x80 = pci_read_config32(f5x_dev, 0x80);
++ cu_enabled = f5x80 & 0xf;
++ if (cu_enabled == 0x1)
++ compute_unit_count = 1;
++ if (cu_enabled == 0x3)
++ compute_unit_count = 2;
++ if (cu_enabled == 0x7)
++ compute_unit_count = 3;
++ if (cu_enabled == 0xf)
++ compute_unit_count = 4;
++
++ /* Disable BAN mode */
++ dword = pci_read_config32(f3x_dev, 0x1b8);
++ dword &= ~(0x7 << 19); /* L3BanMode = 0x0 */
++ pci_write_config32(f3x_dev, 0x1b8, dword);
++
++ /* Set up cache mapping */
++ dword = pci_read_config32(f4x_dev, 0x1d4);
++ if (compute_unit_count == 1) {
++ dword |= 0xf; /* ComputeUnit0SubCacheEn = 0xf */
++ }
++ if (compute_unit_count == 2) {
++ dword &= ~(0xf << 4); /* ComputeUnit1SubCacheEn = 0xc */
++ dword |= (0xc << 4);
++ dword &= ~0xf; /* ComputeUnit0SubCacheEn = 0x3 */
++ dword |= 0x3;
++ }
++ if (compute_unit_count == 3) {
++ dword &= ~(0xf << 8); /* ComputeUnit2SubCacheEn = 0x8 */
++ dword |= (0x8 << 8);
++ dword &= ~(0xf << 4); /* ComputeUnit1SubCacheEn = 0x4 */
++ dword |= (0x4 << 4);
++ dword &= ~0xf; /* ComputeUnit0SubCacheEn = 0x3 */
++ dword |= 0x3;
++ }
++ if (compute_unit_count == 4) {
++ dword &= ~(0xf << 12); /* ComputeUnit3SubCacheEn = 0x8 */
++ dword |= (0x8 << 12);
++ dword &= ~(0xf << 8); /* ComputeUnit2SubCacheEn = 0x4 */
++ dword |= (0x4 << 8);
++ dword &= ~(0xf << 4); /* ComputeUnit1SubCacheEn = 0x2 */
++ dword |= (0x2 << 4);
++ dword &= ~0xf; /* ComputeUnit0SubCacheEn = 0x1 */
++ dword |= 0x1;
++ }
++ pci_write_config32(f4x_dev, 0x1d4, dword);
++
++ /* Enable cache partitioning */
++ pci_write_config32(f4x_dev, 0x1d4, dword);
++ if (compute_unit_count == 1) {
++ dword &= ~(0xf << 26); /* MaskUpdateForComputeUnit = 0x1 */
++ dword |= (0x1 << 26);
++ } else if (compute_unit_count == 2) {
++ dword &= ~(0xf << 26); /* MaskUpdateForComputeUnit = 0x3 */
++ dword |= (0x3 << 26);
++ } else if (compute_unit_count == 3) {
++ dword &= ~(0xf << 26); /* MaskUpdateForComputeUnit = 0x7 */
++ dword |= (0x7 << 26);
++ } else if (compute_unit_count == 4) {
++ dword |= (0xf << 26); /* MaskUpdateForComputeUnit = 0xf */
++ }
++ pci_write_config32(f4x_dev, 0x1d4, dword);
++ }
++ }
++}
++
+ static void cpu_bus_init(device_t dev)
+ {
+ detect_and_enable_probe_filter(dev);
++ detect_and_enable_cache_partitioning(dev);
+ initialize_cpus(dev->link_list);
+ #if CONFIG_AMD_SB_CIMX
+ sb_After_Pci_Init();
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0106-amd-amdmct-mct_ddr3-Set-prefetch-double-stride-to-im.patch b/resources/libreboot/patch/kgpe-d16/0106-amd-amdmct-mct_ddr3-Set-prefetch-double-stride-to-im.patch
new file mode 100644
index 00000000..94548236
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0106-amd-amdmct-mct_ddr3-Set-prefetch-double-stride-to-im.patch
@@ -0,0 +1,25 @@
+From cd93f46100c1141dba544518c552645cc657406a Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 20:29:55 -0500
+Subject: [PATCH 106/146] amd/amdmct/mct_ddr3: Set prefetch double stride to
+ improve performance
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 166cd3d..a9c148d 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -5550,6 +5550,7 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ val &= ~(0x7 << 8); /* CohPrefPrbLmt = 0x1 */
+ val |= (0x1 << 8);
+ val |= (0x1 << 12); /* EnSplitDctLimits = 0x1 */
++ val |= (0x1 << 20); /* DblPrefEn = 0x1 */
+ val |= (0x7 << 22); /* PrefFourConf = 0x7 */
+ val |= (0x7 << 25); /* PrefFiveConf = 0x7 */
+ val &= ~(0xf << 28); /* DcqBwThrotWm = 0x0 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0107-cpu-amd-model_10xxx-Set-up-Family-15h-Link-Base-Chan.patch b/resources/libreboot/patch/kgpe-d16/0107-cpu-amd-model_10xxx-Set-up-Family-15h-Link-Base-Chan.patch
new file mode 100644
index 00000000..1659debf
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0107-cpu-amd-model_10xxx-Set-up-Family-15h-Link-Base-Chan.patch
@@ -0,0 +1,190 @@
+From 20658b7ba96f2a132ef78bf7a57c6b714ee5a57b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 20:30:36 -0500
+Subject: [PATCH 107/146] cpu/amd/model_10xxx: Set up Family 15h Link Base
+ Channel Buffer Count registers
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 159 ++++++++++++++++++++++++++++++++++-
+ 1 file changed, 155 insertions(+), 4 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 7e39c6d..025fe10 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1200,6 +1200,161 @@ static void cpuSetAMDPCI(u8 node)
+ dword |= (compute_unit_buffer_count << 4);
+ pci_write_config32(NODE_PCI(node, 3), 0x1a0, dword);
+
++ uint8_t link;
++ uint8_t ganged;
++ uint8_t iolink;
++ uint8_t probe_filter_enabled = !!dual_node;
++
++ /* Set up the Link Base Channel Buffer Count */
++ uint8_t isoc_rsp_data;
++ uint8_t isoc_np_req_data;
++ uint8_t isoc_rsp_cmd;
++ uint8_t isoc_preq;
++ uint8_t isoc_np_req_cmd;
++ uint8_t free_data;
++ uint8_t free_cmd;
++ uint8_t rsp_data;
++ uint8_t np_req_data;
++ uint8_t probe_cmd;
++ uint8_t rsp_cmd;
++ uint8_t preq;
++ uint8_t np_req_cmd;
++
++ for (link = 0; link < 4; link++) {
++ if (AMD_CpuFindCapability(node, link, &offset)) {
++ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
++ iolink = (AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++
++ if (!iolink && ganged) {
++ if (probe_filter_enabled) {
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 0;
++ free_cmd = 8;
++ rsp_data = 3;
++ np_req_data = 3;
++ probe_cmd = 4;
++ rsp_cmd = 9;
++ preq = 2;
++ np_req_cmd = 8;
++ } else {
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 0;
++ free_cmd = 8;
++ rsp_data = 3;
++ np_req_data = 3;
++ probe_cmd = 8;
++ rsp_cmd = 9;
++ preq = 2;
++ np_req_cmd = 4;
++ }
++ } else if (!iolink && !ganged) {
++ if (probe_filter_enabled) {
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 0;
++ free_cmd = 8;
++ rsp_data = 3;
++ np_req_data = 3;
++ probe_cmd = 4;
++ rsp_cmd = 9;
++ preq = 2;
++ np_req_cmd = 8;
++ } else {
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 0;
++ free_cmd = 4;
++ rsp_data = 3;
++ np_req_data = 3;
++ probe_cmd = 4;
++ rsp_cmd = 9;
++ preq = 2;
++ np_req_cmd = 4;
++ }
++ } else if (iolink && ganged) {
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 0;
++ free_cmd = 8;
++ rsp_data = 1;
++ np_req_data = 0;
++ probe_cmd = 0;
++ rsp_cmd = 2;
++ preq = 7;
++ np_req_cmd = 14;
++ } else {
++ /* FIXME
++ * This is an educated guess as the BKDG does not specify
++ * the appropriate buffer counts for this case!
++ */
++ isoc_rsp_data = 0;
++ isoc_np_req_data = 0;
++ isoc_rsp_cmd = 0;
++ isoc_preq = 0;
++ isoc_np_req_cmd = 1;
++ free_data = 1;
++ free_cmd = 8;
++ rsp_data = 1;
++ np_req_data = 1;
++ probe_cmd = 0;
++ rsp_cmd = 2;
++ preq = 4;
++ np_req_cmd = 12;
++ }
++
++ dword = pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x94);
++ dword &= ~(0x3 << 27); /* IsocRspData = isoc_rsp_data */
++ dword |= ((isoc_rsp_data & 0x3) << 27);
++ dword &= ~(0x3 << 25); /* IsocNpReqData = isoc_np_req_data */
++ dword |= ((isoc_np_req_data & 0x3) << 25);
++ dword &= ~(0x7 << 22); /* IsocRspCmd = isoc_rsp_cmd */
++ dword |= ((isoc_rsp_cmd & 0x7) << 22);
++ dword &= ~(0x7 << 19); /* IsocPReq = isoc_preq */
++ dword |= ((isoc_preq & 0x7) << 19);
++ dword &= ~(0x7 << 16); /* IsocNpReqCmd = isoc_np_req_cmd */
++ dword |= ((isoc_np_req_cmd & 0x7) << 16);
++ pci_write_config32(NODE_PCI(node, 0), (link * 0x20) + 0x94, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x90);
++ dword &= ~(0x1 << 31); /* LockBc = 0x1 */
++ dword |= ((0x1 & 0x1) << 31);
++ dword &= ~(0x7 << 25); /* FreeData = free_data */
++ dword |= ((free_data & 0x7) << 25);
++ dword &= ~(0x1f << 20); /* FreeCmd = free_cmd */
++ dword |= ((free_cmd & 0x1f) << 20);
++ dword &= ~(0x3 << 18); /* RspData = rsp_data */
++ dword |= ((rsp_data & 0x3) << 18);
++ dword &= ~(0x3 << 16); /* NpReqData = np_req_data */
++ dword |= ((np_req_data & 0x3) << 16);
++ dword &= ~(0xf << 12); /* ProbeCmd = probe_cmd */
++ dword |= ((probe_cmd & 0xf) << 12);
++ dword &= ~(0xf << 8); /* RspCmd = rsp_cmd */
++ dword |= ((rsp_cmd & 0xf) << 8);
++ dword &= ~(0x7 << 5); /* PReq = preq */
++ dword |= ((preq & 0x7) << 5);
++ dword &= ~(0x1f << 0); /* NpReqCmd = np_req_cmd */
++ dword |= ((np_req_cmd & 0x1f) << 0);
++ pci_write_config32(NODE_PCI(node, 0), (link * 0x20) + 0x90, dword);
++ }
++ }
++
+ /* Set up the Link to XCS Token Counts */
+ uint8_t isoc_rsp_tok_1;
+ uint8_t isoc_preq_tok_1;
+@@ -1217,10 +1372,6 @@ static void cpuSetAMDPCI(u8 node)
+ uint8_t preq_tok_0;
+ uint8_t req_tok_0;
+
+- uint8_t link;
+- uint8_t ganged;
+- uint8_t iolink;
+- uint8_t probe_filter_enabled = !!dual_node;
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0108-cpu-amd-model_10xxx-Set-up-cache-controls-on-Family-.patch b/resources/libreboot/patch/kgpe-d16/0108-cpu-amd-model_10xxx-Set-up-cache-controls-on-Family-.patch
new file mode 100644
index 00000000..77206662
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0108-cpu-amd-model_10xxx-Set-up-cache-controls-on-Family-.patch
@@ -0,0 +1,41 @@
+From 6b299238948c1cdde372472e3854a79eb6233564 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 20:31:03 -0500
+Subject: [PATCH 108/146] cpu/amd/model_10xxx: Set up cache controls on Family
+ 15h to improve performance
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 5ab4335..ce25b25 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -139,8 +139,9 @@ static const struct {
+ 0x00000000, 1 << (42-32)}, /* Bx [PwcDisableWalkerSharing]=1 */
+
+ { BU_CFG3, AMD_OR_C0, AMD_PTYPE_ALL,
+- 1 << 22, 0x00000000,
+- 1 << 22, 0x00000000}, /* C0 or above [PfcDoubleStride]=1 */
++ (0x3 << 20) | (0x1 << 22), 0x00000000,
++ (0x3 << 20) | (0x1 << 22), 0x00000000}, /* C0 or above [PfcDoubleStride]=1,
++ PfcStrideMul]=0x3 */
+
+ { EX_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
+ 0x00000000, 1 << (54-32),
+@@ -646,6 +647,11 @@ static const struct {
+ * System software should set F5x88[14] to 1b. */
+ { 5, 0x88, AMD_OR_B2, AMD_PTYPE_ALL,
+ 1 << 14, 1 << 14 },
++
++ /* L3 Control 2 */
++ { 3, 0x1b8, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000090, 0x000001d0 }, /* ImplRdProjDelayThresh = 0x2,
++ ImplRdAnySubUnavail = 0x1 */
+ };
+
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0109-cpu-amd-model_10xxx-Set-up-SRI-to-XCS-Token-Count-re.patch b/resources/libreboot/patch/kgpe-d16/0109-cpu-amd-model_10xxx-Set-up-SRI-to-XCS-Token-Count-re.patch
new file mode 100644
index 00000000..434c3aff
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0109-cpu-amd-model_10xxx-Set-up-SRI-to-XCS-Token-Count-re.patch
@@ -0,0 +1,64 @@
+From 3ed8b16993e2880820bb5115cd007e618daf02db Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sat, 8 Aug 2015 22:14:59 -0500
+Subject: [PATCH 109/146] cpu/amd/model_10xxx: Set up SRI to XCS Token Count
+ registers on Family 15h
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 40 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 025fe10..3990dfc 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1639,6 +1639,46 @@ static void cpuSetAMDPCI(u8 node)
+ pci_write_config32(NODE_PCI(node, 3), (link << 2) + 0x148, dword);
+ }
+ }
++
++ /* Set up the SRI to XCS Token Count */
++ uint8_t free_tok;
++ uint8_t up_rsp_tok;
++
++ /* Set defaults */
++ free_tok = 0xa;
++ up_rsp_tok = 0x3;
++
++ if (!dual_node) {
++ free_tok = 0xa;
++ up_rsp_tok = 0x3;
++ } else {
++ if ((sockets == 1)
++ || ((sockets == 2) && (sockets_populated == 1))) {
++ if (probe_filter_enabled) {
++ free_tok = 0x9;
++ up_rsp_tok = 0x3;
++ } else {
++ free_tok = 0xa;
++ up_rsp_tok = 0x3;
++ }
++ } else if ((sockets == 2) && (sockets_populated == 2)) {
++ free_tok = 0xb;
++ up_rsp_tok = 0x1;
++ } else if ((sockets == 4) && (sockets_populated == 2)) {
++ free_tok = 0xa;
++ up_rsp_tok = 0x3;
++ } else if ((sockets == 4) && (sockets_populated == 4)) {
++ free_tok = 0x9;
++ up_rsp_tok = 0x1;
++ }
++ }
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x140);
++ dword &= ~(0xf << 20); /* FreeTok = free_tok */
++ dword |= ((free_tok & 0xf) << 20);
++ dword &= ~(0x3 << 8); /* UpRspTok = up_rsp_tok */
++ dword |= ((up_rsp_tok & 0x3) << 8);
++ pci_write_config32(NODE_PCI(node, 3), 0x140, dword);
+ }
+
+ printk(BIOS_DEBUG, " done\n");
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0110-amd-amdfam10-Control-Family-15h-cache-partitioning-a.patch b/resources/libreboot/patch/kgpe-d16/0110-amd-amdfam10-Control-Family-15h-cache-partitioning-a.patch
new file mode 100644
index 00000000..59b8739d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0110-amd-amdfam10-Control-Family-15h-cache-partitioning-a.patch
@@ -0,0 +1,175 @@
+From 7a1d7345b6840705dc4b32f7f6296975e2cdca50 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 9 Aug 2015 02:47:51 -0500
+Subject: [PATCH 110/146] amd/amdfam10: Control Family 15h cache partitioning
+ and memory performance via nvram
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 5 ++---
+ src/cpu/amd/model_10xxx/init_cpus.c | 13 +++++++++++++
+ src/mainboard/asus/kgpe-d16/cmos.default | 3 +++
+ src/mainboard/asus/kgpe-d16/cmos.layout | 9 +++++++--
+ src/northbridge/amd/amdfam10/northbridge.c | 22 ++++++++++++++++++++++
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 11 ++++++++++-
+ 6 files changed, 57 insertions(+), 6 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index ce25b25..af59120 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -139,9 +139,8 @@ static const struct {
+ 0x00000000, 1 << (42-32)}, /* Bx [PwcDisableWalkerSharing]=1 */
+
+ { BU_CFG3, AMD_OR_C0, AMD_PTYPE_ALL,
+- (0x3 << 20) | (0x1 << 22), 0x00000000,
+- (0x3 << 20) | (0x1 << 22), 0x00000000}, /* C0 or above [PfcDoubleStride]=1,
+- PfcStrideMul]=0x3 */
++ 1 << 22, 0x00000000,
++ 1 << 22, 0x00000000}, /* C0 or above [PfcDoubleStride]=1 */
+
+ { EX_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
+ 0x00000000, 1 << (54-32),
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 3990dfc..faf5305 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -981,6 +981,13 @@ void cpuSetAMDMSR(uint8_t node_id)
+
+ /* Revision C0 and above */
+ if (revision & AMD_OR_C0) {
++ uint8_t enable_experimental_memory_speed_boost;
++
++ /* Check to see if cache partitioning is allowed */
++ enable_experimental_memory_speed_boost = 0;
++ if (get_option(&nvram, "experimental_memory_speed_boost") == CB_SUCCESS)
++ enable_experimental_memory_speed_boost = !!nvram;
++
+ uint32_t f3x1fc = pci_read_config32(NODE_PCI(node_id, 3), 0x1fc);
+ msr = rdmsr(FP_CFG);
+ msr.hi &= ~(0x7 << (42-32)); /* DiDtCfg4 */
+@@ -1000,6 +1007,12 @@ void cpuSetAMDMSR(uint8_t node_id)
+ msr.lo &= ~(0x1 << 16); /* DiDtMode */
+ msr.lo |= ((f3x1fc & 0x1) << 16);
+ wrmsr(FP_CFG, msr);
++
++ if (enable_experimental_memory_speed_boost) {
++ msr = rdmsr(BU_CFG3);
++ msr.lo |= (0x3 << 20); /* PfcStrideMul = 0x3 */
++ wrmsr(BU_CFG3, msr);
++ }
+ }
+
+ if (revision & (AMD_DR_GT_D0 | AMD_FAM15_ALL)) {
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 9b30b00..0a898bd 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -20,6 +20,9 @@ cpu_cc6_state = Enable
+ sata_ahci_mode = Enable
+ sata_alpm = Disable
+ maximum_p_state_limit = 0xf
++probe_filter = Auto
++l3_cache_partitioning = Disable
+ ieee1394 = Enable
++experimental_memory_speed_boost = Disable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index ec803b6..010d4db 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -49,8 +49,11 @@ entries
+ 468 1 e 1 sata_alpm
+ 469 4 h 0 maximum_p_state_limit
+ 473 2 e 13 dimm_spd_checksum
+-475 1 r 0 allow_spd_nvram_cache_restore
+-477 1 e 1 ieee1394
++475 1 e 14 probe_filter
++476 1 e 1 l3_cache_partitioning
++477 1 e 1 experimental_memory_speed_boost
++478 1 r 0 allow_spd_nvram_cache_restore
++479 1 e 1 ieee1394
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+ # Reserve the extended AMD configuration registers
+@@ -147,6 +150,8 @@ enumerations
+ 13 0 Enforce
+ 13 1 Ignore
+ 13 2 Override
++14 0 Disable
++14 1 Auto
+
+ checksums
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index 74b8709..e374ed4 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -1650,6 +1650,17 @@ static void detect_and_enable_probe_filter(device_t dev)
+ {
+ uint32_t dword;
+
++ uint8_t nvram;
++ uint8_t enable_probe_filter;
++
++ /* Check to see if the probe filter is allowed */
++ enable_probe_filter = 1;
++ if (get_option(&nvram, "probe_filter") == CB_SUCCESS)
++ enable_probe_filter = !!nvram;
++
++ if (!enable_probe_filter)
++ return;
++
+ uint8_t fam15h = 0;
+ uint8_t rev_gte_d = 0;
+ uint8_t dual_node = 0;
+@@ -1810,6 +1821,17 @@ static void detect_and_enable_cache_partitioning(device_t dev)
+ uint8_t i;
+ uint32_t dword;
+
++ uint8_t nvram;
++ uint8_t enable_l3_cache_partitioning;
++
++ /* Check to see if cache partitioning is allowed */
++ enable_l3_cache_partitioning = 0;
++ if (get_option(&nvram, "l3_cache_partitioning") == CB_SUCCESS)
++ enable_l3_cache_partitioning = !!nvram;
++
++ if (!enable_l3_cache_partitioning)
++ return;
++
+ if (is_fam15h()) {
+ printk(BIOS_DEBUG, "Enabling L3 cache partitioning\n");
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index a9c148d..b869647 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -5537,6 +5537,14 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ mct_ExtMCTConfig_Dx(pDCTstat);
+ } else {
+ /* Family 15h CPUs */
++ uint8_t nvram;
++ uint8_t enable_experimental_memory_speed_boost;
++
++ /* Check to see if cache partitioning is allowed */
++ enable_experimental_memory_speed_boost = 0;
++ if (get_option(&nvram, "experimental_memory_speed_boost") == CB_SUCCESS)
++ enable_experimental_memory_speed_boost = !!nvram;
++
+ val = 0x0ce00f00; /* FlushWrOnStpGnt = 0x0 */
+ val |= 0x10 << 2; /* MctWrLimit = 0x10 */
+ val |= 0x1; /* DctWrLimit = 0x1 */
+@@ -5550,7 +5558,8 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ val &= ~(0x7 << 8); /* CohPrefPrbLmt = 0x1 */
+ val |= (0x1 << 8);
+ val |= (0x1 << 12); /* EnSplitDctLimits = 0x1 */
+- val |= (0x1 << 20); /* DblPrefEn = 0x1 */
++ if (enable_experimental_memory_speed_boost)
++ val |= (0x1 << 20); /* DblPrefEn = 0x1 */
+ val |= (0x7 << 22); /* PrefFourConf = 0x7 */
+ val |= (0x7 << 25); /* PrefFiveConf = 0x7 */
+ val &= ~(0xf << 28); /* DcqBwThrotWm = 0x0 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0111-northbridge-amd-amdht-Add-isochronous-setup-support-.patch b/resources/libreboot/patch/kgpe-d16/0111-northbridge-amd-amdht-Add-isochronous-setup-support-.patch
new file mode 100644
index 00000000..f75f4c5c
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0111-northbridge-amd-amdht-Add-isochronous-setup-support-.patch
@@ -0,0 +1,270 @@
+From 98bfc674fb1fad135400503f6f3f4074b4fb7bfa Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:47:48 -0500
+Subject: [PATCH 111/146] northbridge/amd/amdht: Add isochronous setup support
+ for coherent fabric
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 59 +++++++++++++++++++++++++++++++++++
+ src/northbridge/amd/amdht/h3ffeat.h | 3 ++
+ src/northbridge/amd/amdht/h3finit.c | 34 +++++++++++++++++++-
+ src/northbridge/amd/amdht/h3finit.h | 4 ++-
+ src/northbridge/amd/amdht/h3ncmn.c | 26 +++++++++++++--
+ 5 files changed, 122 insertions(+), 4 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index faf5305..0ac893a 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1694,6 +1694,65 @@ static void cpuSetAMDPCI(u8 node)
+ pci_write_config32(NODE_PCI(node, 3), 0x140, dword);
+ }
+
++ uint8_t link;
++ uint8_t isochronous;
++ uint8_t isochronous_link_present;
++
++ /* Set up isochronous buffers if needed */
++ isochronous_link_present = 0;
++ for (link = 0; link < 4; link++) {
++ if (AMD_CpuFindCapability(node, link, &offset)) {
++ isochronous = (pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x84) >> 12) & 0x1;
++
++ if (isochronous)
++ isochronous_link_present = 1;
++ }
++ }
++
++ uint8_t free_tok;
++ uint8_t up_rsp_cbc;
++ uint8_t isoc_preq_cbc;
++ uint8_t isoc_preq_tok;
++ uint8_t xbar_to_sri_free_list_cbc;
++ if (isochronous_link_present) {
++ /* Adjust buffer counts */
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x70);
++ isoc_preq_cbc = (dword >> 24) & 0x7;
++ up_rsp_cbc = (dword >> 16) & 0x7;
++ up_rsp_cbc--;
++ isoc_preq_cbc++;
++ dword &= ~(0x7 << 24); /* IsocPreqCBC = isoc_preq_cbc */
++ dword |= ((isoc_preq_cbc & 0x7) << 24);
++ dword &= ~(0x7 << 16); /* UpRspCBC = up_rsp_cbc */
++ dword |= ((up_rsp_cbc & 0x7) << 16);
++ pci_write_config32(NODE_PCI(node, 3), 0x70, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x74);
++ isoc_preq_cbc = (dword >> 24) & 0x7;
++ isoc_preq_cbc++;
++ dword &= ~(0x7 << 24); /* IsocPreqCBC = isoc_preq_cbc */
++ dword |= (isoc_preq_cbc & 0x7) << 24;
++ pci_write_config32(NODE_PCI(node, 3), 0x74, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x7c);
++ xbar_to_sri_free_list_cbc = dword & 0x1f;
++ xbar_to_sri_free_list_cbc--;
++ dword &= ~0x1f; /* Xbar2SriFreeListCBC = xbar_to_sri_free_list_cbc */
++ dword |= xbar_to_sri_free_list_cbc & 0x1f;
++ pci_write_config32(NODE_PCI(node, 3), 0x7c, dword);
++
++ dword = pci_read_config32(NODE_PCI(node, 3), 0x140);
++ free_tok = (dword >> 20) & 0xf;
++ isoc_preq_tok = (dword >> 14) & 0x3;
++ free_tok--;
++ isoc_preq_tok++;
++ dword &= ~(0xf << 20); /* FreeTok = free_tok */
++ dword |= ((free_tok & 0xf) << 20);
++ dword &= ~(0x3 << 14); /* IsocPreqTok = isoc_preq_tok */
++ dword |= ((isoc_preq_tok & 0x3) << 14);
++ pci_write_config32(NODE_PCI(node, 3), 0x140, dword);
++ }
++
+ printk(BIOS_DEBUG, " done\n");
+ }
+
+diff --git a/src/northbridge/amd/amdht/h3ffeat.h b/src/northbridge/amd/amdht/h3ffeat.h
+index 5dc9916..c523b12 100644
+--- a/src/northbridge/amd/amdht/h3ffeat.h
++++ b/src/northbridge/amd/amdht/h3ffeat.h
+@@ -79,6 +79,7 @@
+ #define HTSLAVE_LINK01_OFFSET 4
+ #define HTSLAVE_LINK_CONTROL_0_REG 4
+ #define HTSLAVE_FREQ_REV_0_REG 0xC
++#define HTSLAVE_FEATURE_CAP_REG 0x10
+
+ /* HT3 gen Capability */
+ #define IS_HT_GEN3_CAPABILITY(reg) \
+@@ -126,10 +127,12 @@ typedef struct
+ u8 SelWidthIn;
+ u8 SelWidthOut;
+ u8 SelFrequency;
++ uint8_t enable_isochronous_mode;
+
+ /* This section is for keeping track of capabilities and possible configurations */
+ BOOL RegangCap;
+ uint32_t PrvFrequencyCap;
++ uint32_t PrvFeatureCap;
+ u8 PrvWidthInCap;
+ u8 PrvWidthOutCap;
+ uint32_t CompositeFrequencyCap;
+diff --git a/src/northbridge/amd/amdht/h3finit.c b/src/northbridge/amd/amdht/h3finit.c
+index 14f348f..9d1e7d7 100644
+--- a/src/northbridge/amd/amdht/h3finit.c
++++ b/src/northbridge/amd/amdht/h3finit.c
+@@ -1419,6 +1419,38 @@ static void regangLinks(sMainData *pDat)
+ #endif /* HT_BUILD_NC_ONLY */
+ }
+
++static void detectIoLinkIsochronousCapable(sMainData *pDat)
++{
++ uint8_t i;
++ unsigned char iommu;
++ uint8_t isochronous_capable = 0;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ for (i = 0; i < pDat->TotalLinks*2; i += 2) {
++ if ((pDat->PortList[i].Type == PORTLIST_TYPE_CPU) && (pDat->PortList[i+1].Type == PORTLIST_TYPE_IO)) {
++ if ((pDat->PortList[i].PrvFeatureCap & 0x1) && (pDat->PortList[i+1].PrvFeatureCap & 0x1)) {
++ pDat->PortList[i].enable_isochronous_mode = 1;
++ pDat->PortList[i+1].enable_isochronous_mode = 1;
++ isochronous_capable = 1;
++ } else {
++ pDat->PortList[i].enable_isochronous_mode = 0;
++ pDat->PortList[i+1].enable_isochronous_mode = 0;
++ }
++ }
++ }
++
++ if (isochronous_capable && iommu) {
++ printk(BIOS_DEBUG, "Forcing HT links to isochronous mode due to enabled IOMMU\n");
++ /* Isochronous mode must be set on all links if the IOMMU is enabled */
++ for (i = 0; i < pDat->TotalLinks*2; i += 2) {
++ pDat->PortList[i].enable_isochronous_mode = 1;
++ pDat->PortList[i+1].enable_isochronous_mode = 1;
++ }
++ }
++}
++
+ /*----------------------------------------------------------------------------------------
+ * void
+ * selectOptimalWidthAndFrequency(sMainData *pDat)
+@@ -1539,7 +1571,6 @@ static void selectOptimalWidthAndFrequency(sMainData *pDat)
+ temp = cbPCBBAUpstreamWidth;
+ pDat->PortList[i].SelWidthIn = (u8)temp;
+ pDat->PortList[i+1].SelWidthOut = (u8)temp;
+-
+ }
+ }
+
+@@ -1701,6 +1732,7 @@ static void linkOptimization(sMainData *pDat)
+ {
+ pDat->nb->gatherLinkData(pDat, pDat->nb);
+ regangLinks(pDat);
++ detectIoLinkIsochronousCapable(pDat);
+ selectOptimalWidthAndFrequency(pDat);
+ hammerSublinkFixup(pDat);
+ pDat->nb->setLinkData(pDat, pDat->nb);
+diff --git a/src/northbridge/amd/amdht/h3finit.h b/src/northbridge/amd/amdht/h3finit.h
+index 462f3e3..eef8fe7 100644
+--- a/src/northbridge/amd/amdht/h3finit.h
++++ b/src/northbridge/amd/amdht/h3finit.h
+@@ -231,6 +231,7 @@ typedef struct {
+ * @param[in,out] u8* LinkWidthIn = modify to change the Link Witdh In
+ * @param[in,out] u8* LinkWidthOut = modify to change the Link Witdh Out
+ * @param[in,out] u32* FreqCap = modify to change the link's frequency capability
++ * @param[in,out] u32* FeatureCap = modify to change the link's feature capability
+ *
+ * ---------------------------------------------------------------------------------------
+ */
+@@ -245,7 +246,8 @@ typedef struct {
+ u8 Link,
+ u8 *LinkWidthIn,
+ u8 *LinkWidthOut,
+- u32 *FreqCap
++ u32 *FreqCap,
++ u32 *FeatureCap
+ );
+
+ /**----------------------------------------------------------------------------------------
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index e377ff2..841fc0c 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -1433,12 +1433,15 @@ static void gatherLinkData(sMainData *pDat, cNorthBridge *nb)
+ temp &= 0x7; /* Mask off reserved values */
+ pDat->PortList[i].PrvFrequencyCap |= (temp << 17);
+ }
++
++ AmdPCIReadBits(linkBase + HTHOST_FEATURE_CAP_REG, 9, 0, &temp);
++ pDat->PortList[i].PrvFeatureCap = (u16)temp;
+ }
+ else
+ {
+ linkBase = pDat->PortList[i].Pointer;
+ if (pDat->PortList[i].Link == 1)
+- linkBase += HTSLAVE_LINK01_OFFSET;
++ linkBase += HTSLAVE_LINK01_OFFSET;
+
+ AmdPCIReadBits(linkBase + HTSLAVE_LINK_CONTROL_0_REG, 22, 20, &temp);
+ pDat->PortList[i].PrvWidthOutCap = convertBitsToWidth((u8)temp, pDat->nb);
+@@ -1449,6 +1452,9 @@ static void gatherLinkData(sMainData *pDat, cNorthBridge *nb)
+ AmdPCIReadBits(linkBase + HTSLAVE_FREQ_REV_0_REG, 31, 16, &temp);
+ pDat->PortList[i].PrvFrequencyCap = (u16)temp;
+
++ AmdPCIReadBits(linkBase + HTSLAVE_FEATURE_CAP_REG, 7, 0, &temp);
++ pDat->PortList[i].PrvFeatureCap = (u16)temp;
++
+ if (pDat->HtBlock->AMD_CB_DeviceCapOverride)
+ {
+ linkBase &= 0xFFFFF000;
+@@ -1465,7 +1471,8 @@ static void gatherLinkData(sMainData *pDat, cNorthBridge *nb)
+ pDat->PortList[i].Link,
+ &(pDat->PortList[i].PrvWidthInCap),
+ &(pDat->PortList[i].PrvWidthOutCap),
+- &(pDat->PortList[i].PrvFrequencyCap));
++ &(pDat->PortList[i].PrvFrequencyCap),
++ &(pDat->PortList[i].PrvFeatureCap));
+ }
+ }
+ }
+@@ -1566,6 +1573,14 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ if (is_gt_rev_d())
+ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG_2, 0, 0, &temp2);
+ AmdPCIWriteBits(linkBase + HTHOST_FREQ_REV_REG, 11, 8, &temp);
++
++ /* Enable isochronous flow control mode if supported by chipset */
++ if (pDat->PortList[i].enable_isochronous_mode)
++ temp = 1;
++ else
++ temp = 0;
++ setHtControlRegisterBits(linkBase + HTHOST_LINK_CONTROL_REG, 12, 12, &temp);
++
+ if (frequency_index > HT_FREQUENCY_1000M) /* Gen1 = 200MHz -> 1000MHz, Gen3 = 1200MHz -> 3200MHz */
+ {
+ /* Enable for Gen3 frequencies */
+@@ -1583,6 +1598,7 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ CPU_HTNB_FUNC_00,
+ REG_HT_LINK_RETRY0_0X130 + 4*pDat->PortList[i].Link),
+ 0, 0, &temp);
++
+ /* and Scrambling enable / disable */
+ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(pDat->PortList[i].NodeID),
+ makePCIBusFromNode(pDat->PortList[i].NodeID),
+@@ -1621,6 +1637,12 @@ static void setLinkData(sMainData *pDat, cNorthBridge *nb)
+ bits = 0;
+ }
+
++ /* Enable isochronous flow control mode if supported by chipset */
++ if (pDat->PortList[i].enable_isochronous_mode)
++ temp = 1;
++ else
++ temp = 0;
++
+ /* Retry Enable */
+ isFound = FALSE;
+ currentPtr = linkBase & (u32)0xFFFFF000; /* Set PCI Offset to 0 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0112-arch-x86-acpi-Add-IVRS-table-generation-routines.patch b/resources/libreboot/patch/kgpe-d16/0112-arch-x86-acpi-Add-IVRS-table-generation-routines.patch
new file mode 100644
index 00000000..92e241cc
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0112-arch-x86-acpi-Add-IVRS-table-generation-routines.patch
@@ -0,0 +1,119 @@
+From e8145c0398d63b35ac486bacd181ef26cc7b8707 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:48:32 -0500
+Subject: [PATCH 112/146] arch/x86/acpi: Add IVRS table generation routines
+
+---
+ src/arch/x86/acpi.c | 25 +++++++++++++++++++++++++
+ src/arch/x86/include/arch/acpi.h | 31 +++++++++++++++++++++++++++++++
+ 2 files changed, 56 insertions(+)
+
+diff --git a/src/arch/x86/acpi.c b/src/arch/x86/acpi.c
+index 417a322..e73e5f2 100644
+--- a/src/arch/x86/acpi.c
++++ b/src/arch/x86/acpi.c
+@@ -6,6 +6,7 @@
+ *
+ * Copyright (C) 2004 SUSE LINUX AG
+ * Copyright (C) 2005-2009 coresystems GmbH
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * ACPI FADT, FACS, and DSDT table support added by
+ * Nick Barker <nick.barker9@btinternet.com>, and those portions
+@@ -506,6 +507,30 @@ void acpi_create_hpet(acpi_hpet_t *hpet)
+ header->checksum = acpi_checksum((void *)hpet, sizeof(acpi_hpet_t));
+ }
+
++void acpi_create_ivrs(acpi_ivrs_t *ivrs,
++ unsigned long (*acpi_fill_ivrs)(acpi_ivrs_t* ivrs_struct, unsigned long current))
++{
++ acpi_header_t *header = &(ivrs->header);
++ unsigned long current = (unsigned long)ivrs + sizeof(acpi_ivrs_t);
++
++ memset((void *)ivrs, 0, sizeof(acpi_ivrs_t));
++
++ /* Fill out header fields. */
++ memcpy(header->signature, "IVRS", 4);
++ memcpy(header->oem_id, OEM_ID, 6);
++ memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
++ memcpy(header->asl_compiler_id, ASLC, 4);
++
++ header->length = sizeof(acpi_ivrs_t);
++ header->revision = 1; /* ACPI 1.0: N/A, ACPI 2.0/3.0/4.0: 1 */
++
++ current = acpi_fill_ivrs(ivrs, current);
++
++ /* (Re)calculate length and checksum. */
++ header->length = current - (unsigned long)ivrs;
++ header->checksum = acpi_checksum((void *)ivrs, header->length);
++}
++
+ unsigned long acpi_write_hpet(device_t device, unsigned long current, acpi_rsdp_t *rsdp)
+ {
+ acpi_hpet_t *hpet;
+diff --git a/src/arch/x86/include/arch/acpi.h b/src/arch/x86/include/arch/acpi.h
+index 28f650c..47bb253 100644
+--- a/src/arch/x86/include/arch/acpi.h
++++ b/src/arch/x86/include/arch/acpi.h
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2004 SUSE LINUX AG
+ * Copyright (C) 2004 Nick Barker
+ * Copyright (C) 2008-2009 coresystems GmbH
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * (Written by Stefan Reinauer <stepan@coresystems.de>)
+ *
+ * This program is free software; you can redistribute it and/or modify
+@@ -194,6 +195,32 @@ typedef struct acpi_madt {
+ u32 flags; /* Multiple APIC flags */
+ } __attribute__ ((packed)) acpi_madt_t;
+
++typedef struct acpi_ivrs_info {
++} __attribute__ ((packed)) acpi_ivrs_info_t;
++
++/* IVRS IVHD (I/O Virtualization Hardware Definition Block) */
++typedef struct acpi_ivrs_ivhd {
++ uint8_t type;
++ uint8_t flags;
++ uint16_t length;
++ uint16_t device_id;
++ uint16_t capability_offset;
++ uint32_t iommu_base_low;
++ uint32_t iommu_base_high;
++ uint16_t pci_segment_group;
++ uint16_t iommu_info;
++ uint32_t efr;
++ uint8_t entry[0];
++} __attribute__ ((packed)) acpi_ivrs_ivhd_t;
++
++/* IVRS (I/O Virtualization Reporting Structure) */
++typedef struct acpi_ivrs {
++ struct acpi_table_header header;
++ uint32_t iv_info;
++ uint32_t reserved[2];
++ struct acpi_ivrs_ivhd ivhd;
++} __attribute__ ((packed)) acpi_ivrs_t;
++
+ enum dev_scope_type {
+ SCOPE_PCI_ENDPOINT = 1,
+ SCOPE_PCI_SUB = 2,
+@@ -497,6 +524,7 @@ unsigned long fw_cfg_acpi_tables(unsigned long start);
+ unsigned long write_acpi_tables(unsigned long addr);
+ unsigned long acpi_fill_madt(unsigned long current);
+ unsigned long acpi_fill_mcfg(unsigned long current);
++unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t* ivrs, unsigned long current);
+ void acpi_create_ssdt_generator(acpi_header_t *ssdt, const char *oem_table_id);
+ void acpi_create_fadt(acpi_fadt_t *fadt,acpi_facs_t *facs, void *dsdt);
+ #if IS_ENABLED(CONFIG_COMMON_FADT)
+@@ -535,6 +563,9 @@ void acpi_create_srat(acpi_srat_t *srat,
+ void acpi_create_slit(acpi_slit_t *slit,
+ unsigned long (*acpi_fill_slit)(unsigned long current));
+
++void acpi_create_ivrs(acpi_ivrs_t *ivrs,
++ unsigned long (*acpi_fill_ivrs)(acpi_ivrs_t* ivrs_struct, unsigned long current));
++
+ #if ENV_RAMSTAGE
+ void acpi_create_hpet(acpi_hpet_t *hpet);
+ unsigned long acpi_write_hpet(device_t device, unsigned long start, acpi_rsdp_t *rsdp);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0113-southbridge-amd-sr5650-Add-IOMMU-support.patch b/resources/libreboot/patch/kgpe-d16/0113-southbridge-amd-sr5650-Add-IOMMU-support.patch
new file mode 100644
index 00000000..8550e61d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0113-southbridge-amd-sr5650-Add-IOMMU-support.patch
@@ -0,0 +1,709 @@
+From c8e26ec0ca3beda98e31823466cc668aa453a083 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:49:06 -0500
+Subject: [PATCH 113/146] southbridge/amd/sr5650: Add IOMMU support
+
+---
+ src/include/device/pci_ids.h | 1 +
+ src/southbridge/amd/sr5650/cmn.h | 3 +
+ src/southbridge/amd/sr5650/early_setup.c | 50 +++-
+ src/southbridge/amd/sr5650/sr5650.c | 479 +++++++++++++++++++++++++++++-
+ src/southbridge/amd/sr5650/sr5650.h | 14 +
+ 5 files changed, 537 insertions(+), 10 deletions(-)
+
+diff --git a/src/include/device/pci_ids.h b/src/include/device/pci_ids.h
+index 664ac49..72f1ece 100644
+--- a/src/include/device/pci_ids.h
++++ b/src/include/device/pci_ids.h
+@@ -429,6 +429,7 @@
+ #define PCI_DEVICE_ID_AMD_SR5650_PCIE_DEV12 0x5A20
+ #define PCI_DEVICE_ID_AMD_SR5650_PCIE_DEV13 0x5A1E
+ #define PCI_DEVICE_ID_AMD_SR5650_PCIE_DEV8 0x5A21
++#define PCI_DEVICE_ID_AMD_SR5650_IOMMU 0x5A23
+
+ #define PCI_DEVICE_ID_AMD_CZ_HDA 0x157A
+ #define PCI_DEVICE_ID_AMD_CZ_LPC 0x790E
+diff --git a/src/southbridge/amd/sr5650/cmn.h b/src/southbridge/amd/sr5650/cmn.h
+index 23d25d5..a54bdc5 100644
+--- a/src/southbridge/amd/sr5650/cmn.h
++++ b/src/southbridge/amd/sr5650/cmn.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -26,6 +27,8 @@
+ #define NBHTIU_INDEX 0x94 /* Note: It is different with RS690, whose HTIU index is 0xA8 */
+ #define NBMC_INDEX 0xE8
+ #define NBPCIE_INDEX 0xE0
++#define L2CFG_INDEX 0xF0
++#define L1CFG_INDEX 0xF8
+ #define EXT_CONF_BASE_ADDRESS CONFIG_MMCONF_BASE_ADDRESS
+ #define TEMP_MMIO_BASE_ADDRESS 0xC0000000
+
+diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
+index 62b0dab..e7cca06 100644
+--- a/src/southbridge/amd/sr5650/early_setup.c
++++ b/src/southbridge/amd/sr5650/early_setup.c
+@@ -24,6 +24,8 @@
+ #include <arch/io.h>
+ #include <console/console.h>
+ #include <cpu/x86/msr.h>
++#include <option.h>
++#include <reset.h>
+ #include "sr5650.h"
+ #include "cmn.h"
+
+@@ -271,6 +273,34 @@ void sr5650_htinit(void)
+ /* HT Buffer Allocation for Ganged Links!!! */
+ #endif /* CONFIG_NORTHBRIDGE_AMD_AMDFAM10 || CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY10 */
+ }
++
++}
++
++/* Must be run immediately after HT setup is complete and first warm reset has occurred (if applicable)
++ * Attempting to switch the NB into isochronous mode before the CPUs have engaged isochronous mode
++ * will cause a system hard lockup...
++ */
++void sr5650_htinit_dect_and_enable_isochronous_link(void)
++{
++ device_t sr5650_f0;
++ unsigned char iommu;
++
++ sr5650_f0 = PCI_DEV(0, 0, 0);
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ if (iommu) {
++ /* Enable isochronous mode */
++ set_nbcfg_enable_bits(sr5650_f0, 0xc8, 1 << 12, 1 << 12);
++
++ /* Apply pending changes */
++ if (!((pci_read_config32(sr5650_f0, 0xc8) >> 12) & 0x1)) {
++ printk(BIOS_INFO, "...WARM RESET...\n\n\n");
++ soft_reset();
++ die("After soft_reset_x - shouldn't see this message!!!\n");
++ }
++ }
+ }
+
+ #if CONFIG_NORTHBRIDGE_AMD_AMDFAM10 || CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY10 /* save some spaces */
+@@ -335,8 +365,21 @@ static void sr5650_por_pcicfg_init(device_t nb_dev)
+ *****************************************/
+ static void sr5650_por_misc_index_init(device_t nb_dev)
+ {
+- /* disable IOMMU */
+- set_nbmisc_enable_bits(nb_dev, 0x75, 0x1, 0x0);
++ unsigned char iommu;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ if (iommu) {
++ /* enable IOMMU */
++ printk(BIOS_DEBUG, "Enabling IOMMU\n");
++ set_nbmisc_enable_bits(nb_dev, 0x75, 0x1, 0x1);
++ } else {
++ /* disable IOMMU */
++ printk(BIOS_DEBUG, "Disabling IOMMU\n");
++ set_nbmisc_enable_bits(nb_dev, 0x75, 0x1, 0x0);
++ }
++
+ /* NBMISCIND:0x75[29]= 1 Device ID for hotplug and PME message */
+ set_nbmisc_enable_bits(nb_dev, 0x75, 1 << 29, 1 << 29);
+ set_nbmisc_enable_bits(nb_dev, 0x75, 1 << 9, 1 << 9); /* no doc reference, comply with BTS */
+@@ -374,10 +417,11 @@ static void sr5650_por_misc_index_init(device_t nb_dev)
+ * HIDE_NB_AGP_CAP ([0], default=1)HIDE
+ * HIDE_P2P_AGP_CAP ([1], default=1)HIDE
+ * HIDE_NB_GART_BAR ([2], default=1)HIDE
++ * HIDE_MMCFG_BAR ([3], default=1)SHOW
+ * AGPMODE30 ([4], default=0)DISABLE
+ * AGP30ENCHANCED ([5], default=0)DISABLE
+ * HIDE_AGP_CAP ([8], default=1)ENABLE */
+- set_nbmisc_enable_bits(nb_dev, 0x00, 0x0000FFFF, 0 << 0 | 1 << 1 | 1 << 2 | 0 << 6);
++ set_nbmisc_enable_bits(nb_dev, 0x00, 0x0000FFFF, 0 << 0 | 1 << 1 | 1 << 2 | 0 << 3 | 0 << 6);
+
+ /* IOC_LAT_PERF_CNTR_CNTL */
+ set_nbmisc_enable_bits(nb_dev, 0x30, 0xFF, 0x00);
+diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
+index 6db1eb1..b296c47 100644
+--- a/src/southbridge/amd/sr5650/sr5650.c
++++ b/src/southbridge/amd/sr5650/sr5650.c
+@@ -26,7 +26,9 @@
+ #include <device/pci_ops.h>
+ #include <cpu/x86/msr.h>
+ #include <cpu/amd/mtrr.h>
++#include <stdlib.h>
+ #include <delay.h>
++#include <option.h>
+ #include "sr5650.h"
+ #include "cmn.h"
+
+@@ -87,6 +89,26 @@ void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data)
+ nb_write_index((nb_dev), NBPCIE_INDEX, (index), (data));
+ }
+
++uint32_t l2cfg_ind_read_index(device_t nb_dev, uint32_t index)
++{
++ return nb_read_index((nb_dev), L2CFG_INDEX, (index));
++}
++
++void l2cfg_ind_write_index(device_t nb_dev, uint32_t index, uint32_t data)
++{
++ nb_write_index((nb_dev), L2CFG_INDEX | (0x1 << 8), (index), (data));
++}
++
++uint32_t l1cfg_ind_read_index(device_t nb_dev, uint32_t index)
++{
++ return nb_read_index((nb_dev), L1CFG_INDEX, (index));
++}
++
++void l1cfg_ind_write_index(device_t nb_dev, uint32_t index, uint32_t data)
++{
++ nb_write_index((nb_dev), L1CFG_INDEX | (0x1 << 31), (index), (data));
++}
++
+ /***********************************************************
+ * To access bar3 we need to program PCI MMIO 7 in K8.
+ * in_out:
+@@ -286,6 +308,240 @@ u32 get_vid_did(device_t dev)
+ return pci_read_config32(dev, 0);
+ }
+
++void detect_and_enable_iommu(device_t iommu_dev) {
++ uint32_t dword;
++ uint8_t l1_target;
++ unsigned char iommu;
++ void * mmio_base;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ if (iommu) {
++ printk(BIOS_DEBUG, "Initializing IOMMU\n");
++
++ device_t nb_dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++
++ if (!nb_dev) {
++ printk(BIOS_WARNING, "Unable to find SR5690 device! IOMMU NOT initialized\n");
++ return;
++ }
++
++ mmio_base = (void*)(pci_read_config32(iommu_dev, 0x44) & 0xffffc000);
++
++ // if (get_nb_rev(nb_dev) == REV_SR5650_A11) {
++ // dword = pci_read_config32(iommu_dev, 0x6c);
++ // dword &= ~(0x1 << 8);
++ // pci_write_config32(iommu_dev, 0x6c, dword);
++ // }
++
++ dword = pci_read_config32(iommu_dev, 0x50);
++ dword &= ~(0x1 << 22);
++ pci_write_config32(iommu_dev, 0x50, dword);
++
++ dword = pci_read_config32(iommu_dev, 0x44);
++ dword |= 0x1;
++ pci_write_config32(iommu_dev, 0x44, dword);
++
++ write32((void*)(mmio_base + 0x8), 0x0);
++ write32((void*)(mmio_base + 0xc), 0x08000000);
++ write32((void*)(mmio_base + 0x10), 0x0);
++ write32((void*)(mmio_base + 0x2008), 0x0);
++ write32((void*)(mmio_base + 0x2010), 0x0);
++
++ /* IOMMU L1 initialization */
++ for (l1_target = 0; l1_target < 6; l1_target++) {
++ dword = l1cfg_ind_read_index(nb_dev, (l1_target << 16) + 0xc);
++ dword |= (0x7 << 28);
++ l1cfg_ind_write_index(nb_dev, (l1_target << 16) + 0xc, dword);
++
++ dword = l1cfg_ind_read_index(nb_dev, (l1_target << 16) + 0x7);
++ dword |= (0x1 << 5);
++ l1cfg_ind_write_index(nb_dev, (l1_target << 16) + 0x7, dword);
++ }
++
++ /* IOMMU L2 initialization */
++ dword = l2cfg_ind_read_index(nb_dev, 0xc);
++ dword |= (0x7 << 29);
++ l2cfg_ind_write_index(nb_dev, 0xc, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x10);
++ dword &= ~(0x3 << 8);
++ dword |= (0x2 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x10, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x14);
++ dword &= ~(0x3 << 8);
++ dword |= (0x2 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x14, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x18);
++ dword &= ~(0x3 << 8);
++ dword |= (0x2 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x18, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x1c);
++ dword &= ~(0x3 << 8);
++ dword |= (0x2 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x1c, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x50);
++ dword &= ~(0x3 << 8);
++ dword |= (0x2 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x50, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x10);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x10, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x14);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x14, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x18);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x18, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x1c);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x1c, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x50);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x50, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x6);
++ dword |= (0x1 << 7);
++ l2cfg_ind_write_index(nb_dev, 0x6, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x44);
++ dword |= (0x1 << 0);
++ l2cfg_ind_write_index(nb_dev, 0x44, dword);
++
++// if (get_nb_rev(nb_dev) == REV_SR5650_A21) {
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 1);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x44);
++ dword |= (0x1 << 1);
++ l2cfg_ind_write_index(nb_dev, 0x44, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 2);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 3);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x44);
++ dword |= (0x1 << 3);
++ l2cfg_ind_write_index(nb_dev, 0x44, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x6);
++ dword |= (0x1 << 5);
++ l2cfg_ind_write_index(nb_dev, 0x6, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x6);
++ dword |= (0x1 << 6);
++ l2cfg_ind_write_index(nb_dev, 0x6, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 5);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x44);
++ dword |= (0x1 << 4);
++ l2cfg_ind_write_index(nb_dev, 0x44, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 6);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x7);
++ dword |= (0x1 << 7);
++ l2cfg_ind_write_index(nb_dev, 0x7, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x6);
++ dword |= (0x1 << 8);
++ l2cfg_ind_write_index(nb_dev, 0x6, dword);
++// }
++
++ l2cfg_ind_write_index(nb_dev, 0x52, 0xf0000002);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x80);
++ dword |= (0x1 << 0);
++ l2cfg_ind_write_index(nb_dev, 0x80, dword);
++
++ dword = l2cfg_ind_read_index(nb_dev, 0x30);
++ dword |= (0x1 << 0);
++ l2cfg_ind_write_index(nb_dev, 0x30, dword);
++ }
++}
++
++void sr5650_iommu_read_resources(device_t dev)
++{
++ unsigned char iommu;
++ struct resource *res;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ /* Get the normal pci resources of this device */
++ pci_dev_read_resources(dev);
++
++ if (iommu) {
++ /* Request MMIO range allocation */
++ res = new_resource(dev, 0x44); /* IOMMU */
++ res->base = 0x0;
++ res->size = 0x4000;
++ res->limit = 0xFFFFFFFFUL; /* res->base + res->size -1; */
++ res->align = 14; /* 16k alignment */
++ res->gran = 14;
++ res->flags = IORESOURCE_MEM | IORESOURCE_RESERVE;
++ }
++
++ compact_resources(dev);
++}
++
++void sr5650_iommu_set_resources(device_t dev)
++{
++ unsigned char iommu;
++ struct resource *res;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ /* Get the normal pci resources of this device */
++ pci_dev_read_resources(dev);
++
++ if (iommu) {
++ /* Get the allocated range */
++ res = find_resource(dev, 0x44);
++
++ if (res->base == 0) {
++ printk(BIOS_WARNING, "Unable to allocate MMIO range to IOMMU\n");
++ }
++
++ /* Assign the range to hardware */
++ pci_write_config32(dev, 0x44, res->base & 0xffffc000);
++ pci_write_config32(dev, 0x48, 0x0);
++ }
++
++ /* Run standard resource set routine */
++ pci_dev_set_resources(dev);
++}
++
++void sr5650_iommu_enable_resources(device_t dev)
++{
++ detect_and_enable_iommu(dev);
++}
++
+ void sr5650_nb_pci_table(device_t nb_dev)
+ { /* NBPOR_InitPOR function. */
+ u8 temp8;
+@@ -365,13 +621,23 @@ void sr5650_enable(device_t dev)
+ dev_ind = dev->path.pci.devfn >> 3;
+ switch (dev_ind) {
+ case 0: /* bus0, dev0, fun0; */
+- printk(BIOS_INFO, "Bus-0, Dev-0, Fun-0.\n");
+- enable_pcie_bar3(nb_dev); /* PCIEMiscInit */
+-
+- config_gpp_core(nb_dev, sb_dev);
+- sr5650_gpp_sb_init(nb_dev, sb_dev, 8);
+-
+- sr5650_nb_pci_table(nb_dev);
++ switch (dev->path.pci.devfn & 0x7) {
++ case 0:
++ printk(BIOS_INFO, "Bus-0, Dev-0, Fun-0.\n");
++ enable_pcie_bar3(nb_dev); /* PCIEMiscInit */
++
++ config_gpp_core(nb_dev, sb_dev);
++ sr5650_gpp_sb_init(nb_dev, sb_dev, 8);
++
++ sr5650_nb_pci_table(nb_dev);
++ break;
++ case 1:
++ printk(BIOS_INFO, "Bus-0, Dev-0, Fun-1.\n");
++ break;
++ case 2:
++ printk(BIOS_INFO, "Bus-0, Dev-0, Fun-2.\n");
++ break;
++ }
+ break;
+
+ case 2: /* bus0, dev2,3 GPP1 */
+@@ -438,6 +704,205 @@ void sr5650_enable(device_t dev)
+ }
+ }
+
++static void add_ivrs_device_entries(struct device *parent, struct device *dev, int depth, int linknum, int8_t *root_level, unsigned long *current, uint16_t *length)
++{
++ uint8_t *p;
++ struct device *sibling;
++ struct bus *link;
++
++ if (!root_level) {
++ root_level = malloc(sizeof(int8_t));
++ *root_level = -1;
++ }
++
++ if (dev->path.type == DEVICE_PATH_PCI) {
++ if ((dev->bus->secondary == 0x0) && (dev->path.pci.devfn == 0x0))
++ *root_level = depth;
++
++ if (*root_level != -1) {
++ if (depth >= *root_level) {
++ if (dev->enabled) {
++ if (depth == *root_level) {
++ if (dev->path.pci.devfn < (0x1 << 3)) {
++ /* SR5690 control device */
++ } else if ((dev->path.pci.devfn >= (0x1 << 3)) && (dev->path.pci.devfn < (0xe << 3))) {
++ /* SR5690 PCIe bridge device */
++ } else {
++ if (dev->path.pci.devfn == (0x14 << 3)) {
++ /* SMBUS controller */
++ p = (uint8_t *) *current;
++ p[0] = 0x2; /* Entry type */
++ p[1] = dev->path.pci.devfn; /* Device */
++ p[2] = dev->bus->secondary; /* Bus */
++ p[3] = 0x97; /* Data */
++ p[4] = 0x0; /* Padding */
++ p[5] = 0x0; /* Padding */
++ p[6] = 0x0; /* Padding */
++ p[7] = 0x0; /* Padding */
++ *length += 8;
++ *current += 8;
++ } else {
++ /* Other southbridge device */
++ p = (uint8_t *) *current;
++ p[0] = 0x2; /* Entry type */
++ p[1] = dev->path.pci.devfn; /* Device */
++ p[2] = dev->bus->secondary; /* Bus */
++ p[3] = 0x0; /* Data */
++ p[4] = 0x0; /* Padding */
++ p[5] = 0x0; /* Padding */
++ p[6] = 0x0; /* Padding */
++ p[7] = 0x0; /* Padding */
++ *length += 8;
++ *current += 8;
++ }
++ }
++ } else {
++ if ((dev->hdr_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) {
++ /* Device behind bridge */
++ if (pci_find_capability(dev, PCI_CAP_ID_PCIE)) {
++ /* Device is PCIe */
++ p = (uint8_t *) *current;
++ p[0] = 0x2; /* Entry type */
++ p[1] = dev->path.pci.devfn; /* Device */
++ p[2] = dev->bus->secondary; /* Bus */
++ p[3] = 0x0; /* Data */
++ p[4] = 0x0; /* Padding */
++ p[5] = 0x0; /* Padding */
++ p[6] = 0x0; /* Padding */
++ p[7] = 0x0; /* Padding */
++ *length += 8;
++ *current += 8;
++ } else {
++ /* Device is legacy PCI or PCI-X */
++ p = (uint8_t *) *current;
++ p[0] = 0x42; /* Entry type */
++ p[1] = dev->path.pci.devfn; /* Device */
++ p[2] = dev->bus->secondary; /* Bus */
++ p[3] = 0x0; /* Data */
++ p[4] = 0x0; /* Reserved */
++ p[5] = parent->path.pci.devfn; /* Device */
++ p[6] = parent->bus->secondary; /* Bus */
++ p[7] = 0x0; /* Reserved */
++ *length += 8;
++ *current += 8;
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++
++ for (link = dev->link_list; link; link = link->next)
++ for (sibling = link->children; sibling; sibling = sibling->sibling)
++ add_ivrs_device_entries(dev, sibling, depth + 1, depth, root_level, current, length);
++
++ free(root_level);
++}
++
++static unsigned long acpi_fill_ivrs(acpi_ivrs_t* ivrs, unsigned long current)
++{
++ uint8_t *p;
++
++ device_t nb_dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ if (!nb_dev) {
++ printk(BIOS_WARNING, "acpi_fill_ivrs: Unable to locate SR5650 device! IVRS table not generated...\n");
++ return (unsigned long)ivrs;
++ }
++
++ device_t iommu_dev = dev_find_slot(0, PCI_DEVFN(0, 2));
++ if (!iommu_dev) {
++ printk(BIOS_WARNING, "acpi_fill_ivrs: Unable to locate SR5650 IOMMU device! IVRS table not generated...\n");
++ return (unsigned long)ivrs;
++ }
++
++ ivrs->iv_info = 0x0;
++ ivrs->iv_info |= (0x40 << 15); /* Maximum supported virtual address size */
++ ivrs->iv_info |= (0x34 << 8); /* Maximum supported physical address size */
++
++ ivrs->ivhd.type = 0x10;
++ ivrs->ivhd.flags = 0x0e;
++ // if (get_nb_rev(nb_dev) != REV_SR5650_A11) {
++ ivrs->ivhd.flags |= 0x10; /* Enable ATS support on all revisions except A11 */
++ // }
++ ivrs->ivhd.length = sizeof(struct acpi_ivrs_ivhd);
++ ivrs->ivhd.device_id = 0x2 | (nb_dev->bus->secondary << 8); /* BDF <bus>:00.2 */
++ ivrs->ivhd.capability_offset = 0x40; /* Capability block 0x40 (type 0xf, "Secure device") */
++ ivrs->ivhd.iommu_base_low = pci_read_config32(iommu_dev, 0x44) & 0xffffc000;
++ ivrs->ivhd.iommu_base_high = pci_read_config32(iommu_dev, 0x48);
++ ivrs->ivhd.pci_segment_group = 0x0;
++ ivrs->ivhd.iommu_info = 0x0;
++ ivrs->ivhd.iommu_info |= (0x14 << 8);
++ ivrs->ivhd.efr = 0x0;
++
++ /* Describe HPET */
++ p = (uint8_t *)current;
++ p[0] = 0x48; /* Entry type */
++ p[1] = 0; /* Device */
++ p[2] = 0; /* Bus */
++ p[3] = 0xd7; /* Data */
++ p[4] = 0x0; /* HPET number */
++ p[5] = 0x14 << 3; /* HPET device */
++ p[6] = nb_dev->bus->secondary; /* HPET bus */
++ p[7] = 0x2; /* Variety */
++ ivrs->ivhd.length += 8;
++ current += 8;
++
++ /* Describe PCI devices */
++ add_ivrs_device_entries(NULL, all_devices, 0, -1, NULL, &current, &ivrs->ivhd.length);
++
++ /* Describe IOAPICs */
++ unsigned long prev_current = current;
++ current = acpi_fill_ivrs_ioapic(ivrs, current);
++ ivrs->ivhd.length += (current - prev_current);
++
++ return current;
++}
++
++unsigned long southbridge_write_acpi_tables(device_t device,
++ unsigned long current,
++ struct acpi_rsdp *rsdp)
++{
++ unsigned char iommu;
++
++ iommu = 1;
++ get_option(&iommu, "iommu");
++
++ if (iommu) {
++ acpi_ivrs_t *ivrs;
++
++ /* IVRS */
++ current = ALIGN(current, 8);
++ printk(BIOS_DEBUG, "ACPI: * IVRS at %lx\n", current);
++ ivrs = (acpi_ivrs_t *) current;
++ acpi_create_ivrs(ivrs, acpi_fill_ivrs);
++ current += ivrs->header.length;
++ acpi_add_table(rsdp, ivrs);
++ }
++
++ return current;
++}
++
++static struct pci_operations iommu_ops_pci = {
++ .set_subsystem = pci_dev_set_subsystem,
++};
++
++static struct device_operations iommu_ops = {
++ .read_resources = sr5650_iommu_read_resources,
++ .set_resources = sr5650_iommu_set_resources,
++ .enable_resources = sr5650_iommu_enable_resources,
++ .write_acpi_tables = southbridge_write_acpi_tables,
++ .init = 0,
++ .scan_bus = 0,
++ .ops_pci = &iommu_ops_pci,
++};
++
++static const struct pci_driver ht_driver_sr5690 __pci_driver = {
++ .ops = &iommu_ops,
++ .vendor = PCI_VENDOR_ID_ATI,
++ .device = PCI_DEVICE_ID_AMD_SR5650_IOMMU,
++};
++
+ struct chip_operations southbridge_amd_sr5650_ops = {
+ CHIP_NAME("ATI SR5650")
+ .enable_dev = sr5650_enable,
+diff --git a/src/southbridge/amd/sr5650/sr5650.h b/src/southbridge/amd/sr5650/sr5650.h
+index ebbde41..a3518fb 100644
+--- a/src/southbridge/amd/sr5650/sr5650.h
++++ b/src/southbridge/amd/sr5650/sr5650.h
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -21,6 +22,7 @@
+ #define __SR5650_H__
+
+ #include <stdint.h>
++#include <arch/acpi.h>
+ #include <device/pci_ids.h>
+ #include "chip.h"
+ #include "rev.h"
+@@ -95,16 +97,24 @@ u32 nbpcie_p_read_index(device_t dev, u32 index);
+ void nbpcie_p_write_index(device_t dev, u32 index, u32 data);
+ u32 nbpcie_ind_read_index(device_t nb_dev, u32 index);
+ void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data);
++uint32_t l2cfg_ind_read_index(device_t nb_dev, uint32_t index);
++void l2cfg_ind_write_index(device_t nb_dev, uint32_t index, uint32_t data);
++uint32_t l1cfg_ind_read_index(device_t nb_dev, uint32_t index);
++void l1cfg_ind_write_index(device_t nb_dev, uint32_t index, uint32_t data);
+ u32 pci_ext_read_config32(device_t nb_dev, device_t dev, u32 reg);
+ void pci_ext_write_config32(device_t nb_dev, device_t dev, u32 reg, u32 mask, u32 val);
+ void sr5650_set_tom(device_t nb_dev);
+
++unsigned long southbridge_write_acpi_tables(device_t device, unsigned long current,
++ struct acpi_rsdp *rsdp);
++
+ void ProgK8TempMmioBase(u8 in_out, u32 pcie_base_add, u32 mmio_base_add);
+ void enable_pcie_bar3(device_t nb_dev);
+ void disable_pcie_bar3(device_t nb_dev);
+
+ void enable_sr5650_dev8(void);
+ void sr5650_htinit(void);
++void sr5650_htinit_dect_and_enable_isochronous_link(void);
+ void sr5650_early_setup(void);
+ void sr5650_before_pci_init(void);
+ void sr5650_enable(device_t dev);
+@@ -118,6 +128,10 @@ void pcie_config_misc_clk(device_t nb_dev);
+ void fam10_optimization(void);
+ void sr5650_disable_pcie_bridge(void);
+ u32 get_vid_did(device_t dev);
++void detect_and_enable_iommu(device_t iommu_dev);
++void sr5650_iommu_read_resources(device_t dev);
++void sr5650_iommu_set_resources(device_t dev);
++void sr5650_iommu_enable_resources(device_t dev);
+ void sr5650_nb_pci_table(device_t nb_dev);
+ void init_gen2(device_t nb_dev, device_t dev, u8 port);
+ void sr56x0_lock_hwinitreg(void);
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0114-southbridge-amd-sr5650-Hide-clock-configuration-devi.patch b/resources/libreboot/patch/kgpe-d16/0114-southbridge-amd-sr5650-Hide-clock-configuration-devi.patch
new file mode 100644
index 00000000..ed6ae690
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0114-southbridge-amd-sr5650-Hide-clock-configuration-devi.patch
@@ -0,0 +1,55 @@
+From f2495e7909302bd8cbc0633bde5a9ce60a6336c5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 13 Aug 2015 17:45:12 -0500
+Subject: [PATCH 114/146] southbridge/amd/sr5650: Hide clock configuration
+ device after setup is complete
+
+---
+ src/southbridge/amd/sr5650/early_setup.c | 16 ++++++++--------
+ src/southbridge/amd/sr5650/pcie.c | 3 +++
+ 2 files changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
+index e7cca06..cb666db 100644
+--- a/src/southbridge/amd/sr5650/early_setup.c
++++ b/src/southbridge/amd/sr5650/early_setup.c
+@@ -414,14 +414,14 @@ static void sr5650_por_misc_index_init(device_t nb_dev)
+ set_nbmisc_enable_bits(nb_dev, 0x01, 0xFFFFFFFF, 0x00000310);
+
+ /* NBCFG (NBMISCIND 0x0): NB_CNTL -
+- * HIDE_NB_AGP_CAP ([0], default=1)HIDE
+- * HIDE_P2P_AGP_CAP ([1], default=1)HIDE
+- * HIDE_NB_GART_BAR ([2], default=1)HIDE
+- * HIDE_MMCFG_BAR ([3], default=1)SHOW
+- * AGPMODE30 ([4], default=0)DISABLE
+- * AGP30ENCHANCED ([5], default=0)DISABLE
+- * HIDE_AGP_CAP ([8], default=1)ENABLE */
+- set_nbmisc_enable_bits(nb_dev, 0x00, 0x0000FFFF, 0 << 0 | 1 << 1 | 1 << 2 | 0 << 3 | 0 << 6);
++ * HIDE_NB_AGP_CAP ([0], default=1)HIDE
++ * HIDE_P2P_AGP_CAP ([1], default=1)HIDE
++ * HIDE_NB_GART_BAR ([2], default=1)HIDE
++ * HIDE_MMCFG_BAR ([3], default=1)SHOW
++ * AGPMODE30 ([4], default=0)DISABLE
++ * AGP30ENCHANCED ([5], default=0)DISABLE
++ * HIDE_CLKCFG_HEADER ([8], default=0)SHOW */
++ set_nbmisc_enable_bits(nb_dev, 0x00, 0x0000FFFF, 0 << 0 | 1 << 1 | 1 << 2 | 0 << 3 | 0 << 6 | 0 << 8);
+
+ /* IOC_LAT_PERF_CNTR_CNTL */
+ set_nbmisc_enable_bits(nb_dev, 0x30, 0xFF, 0x00);
+diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
+index 09ce217..360e9cb 100644
+--- a/src/southbridge/amd/sr5650/pcie.c
++++ b/src/southbridge/amd/sr5650/pcie.c
+@@ -854,6 +854,9 @@ void sr56x0_lock_hwinitreg(void)
+
+ /* Lock HWInit Register NBMISCIND:0x0 NBCNTL[7] HWINIT_WR_LOCK */
+ set_nbmisc_enable_bits(nb_dev, 0x00, 1 << 7, 1 << 7);
++
++ /* Hide clock configuration PCI device HIDE_CLKCFG_HEADER */
++ set_nbmisc_enable_bits(nb_dev, 0x00, 0x00000100, 1 << 8);
+ }
+
+ /*****************************************
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0115-northbridge-amd-amdfam10-Rename-mislabeled-iommu-nvr.patch b/resources/libreboot/patch/kgpe-d16/0115-northbridge-amd-amdfam10-Rename-mislabeled-iommu-nvr.patch
new file mode 100644
index 00000000..2169d97e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0115-northbridge-amd-amdfam10-Rename-mislabeled-iommu-nvr.patch
@@ -0,0 +1,53 @@
+From dc91a6e3bdbc54240b7a20ea133ba78c89bafa47 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:52:03 -0500
+Subject: [PATCH 115/146] northbridge/amd/amdfam10: Rename mislabeled iommu
+ nvram option to gart
+
+---
+ src/northbridge/amd/amdfam10/misc_control.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index 1057ac1..4710876 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -43,7 +43,7 @@
+ *
+ * @param dev
+ *
+- * There is only one AGP aperture resource needed. The resoruce is added to
++ * There is only one AGP aperture resource needed. The resource is added to
+ * the northbridge of BSP.
+ *
+ * The same trick can be used to augment legacy VGA resources which can
+@@ -55,7 +55,7 @@
+ static void mcf3_read_resources(device_t dev)
+ {
+ struct resource *resource;
+- unsigned char iommu;
++ unsigned char gart;
+ /* Read the generic PCI resources */
+ pci_dev_read_resources(dev);
+
+@@ -64,13 +64,13 @@ static void mcf3_read_resources(device_t dev)
+ return;
+ }
+
+- iommu = 1;
+- get_option(&iommu, "iommu");
++ gart = 1;
++ get_option(&gart, "gart");
+
+- if (iommu) {
++ if (gart) {
+ /* Add a Gart apeture resource */
+ resource = new_resource(dev, 0x94);
+- resource->size = iommu?CONFIG_AGP_APERTURE_SIZE:1;
++ resource->size = gart?CONFIG_AGP_APERTURE_SIZE:1;
+ resource->align = log2(resource->size);
+ resource->gran = log2(resource->size);
+ resource->limit = 0xffffffff; /* 4G */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0116-northbridge-amd-amdfam10-Fix-gart-setup-not-working-.patch b/resources/libreboot/patch/kgpe-d16/0116-northbridge-amd-amdfam10-Fix-gart-setup-not-working-.patch
new file mode 100644
index 00000000..3427889f
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0116-northbridge-amd-amdfam10-Fix-gart-setup-not-working-.patch
@@ -0,0 +1,94 @@
+From e63ed74fba45c969ebad74153c4644bcfb6229da Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:52:31 -0500
+Subject: [PATCH 116/146] northbridge/amd/amdfam10: Fix gart setup not working
+ on Family 15h processors
+
+---
+ src/northbridge/amd/amdfam10/misc_control.c | 34 ++++++++++++++++++++-------
+ 1 file changed, 26 insertions(+), 8 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index 4710876..61cf1b6 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -78,7 +78,7 @@ static void mcf3_read_resources(device_t dev)
+ }
+ }
+
+-static void set_agp_aperture(device_t dev)
++static void set_agp_aperture(device_t dev, uint32_t pci_id)
+ {
+ struct resource *resource;
+
+@@ -98,7 +98,7 @@ static void set_agp_aperture(device_t dev)
+
+ /* Update the other northbriges */
+ pdev = 0;
+- while((pdev = dev_find_device(PCI_VENDOR_ID_AMD, 0x1203, pdev))) {
++ while ((pdev = dev_find_device(PCI_VENDOR_ID_AMD, pci_id, pdev))) {
+ /* Store the GART size but don't enable it */
+ pci_write_config32(pdev, 0x90, gart_acr);
+
+@@ -114,10 +114,19 @@ static void set_agp_aperture(device_t dev)
+ }
+ }
+
+-static void mcf3_set_resources(device_t dev)
++static void mcf3_set_resources_fam10h(device_t dev)
+ {
+ /* Set the gart apeture */
+- set_agp_aperture(dev);
++ set_agp_aperture(dev, 0x1203);
++
++ /* Set the generic PCI resources */
++ pci_dev_set_resources(dev);
++}
++
++static void mcf3_set_resources_fam15h(device_t dev)
++{
++ /* Set the gart apeture */
++ set_agp_aperture(dev, 0x1603);
+
+ /* Set the generic PCI resources */
+ pci_dev_set_resources(dev);
+@@ -156,9 +165,18 @@ static void misc_control_init(struct device *dev)
+ }
+
+
+-static struct device_operations mcf3_ops = {
++static struct device_operations mcf3_ops_fam10h = {
++ .read_resources = mcf3_read_resources,
++ .set_resources = mcf3_set_resources_fam10h,
++ .enable_resources = pci_dev_enable_resources,
++ .init = misc_control_init,
++ .scan_bus = 0,
++ .ops_pci = 0,
++};
++
++static struct device_operations mcf3_ops_fam15h = {
+ .read_resources = mcf3_read_resources,
+- .set_resources = mcf3_set_resources,
++ .set_resources = mcf3_set_resources_fam15h,
+ .enable_resources = pci_dev_enable_resources,
+ .init = misc_control_init,
+ .scan_bus = 0,
+@@ -166,13 +184,13 @@ static struct device_operations mcf3_ops = {
+ };
+
+ static const struct pci_driver mcf3_driver __pci_driver = {
+- .ops = &mcf3_ops,
++ .ops = &mcf3_ops_fam10h,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = 0x1203,
+ };
+
+ static const struct pci_driver mcf3_driver_fam15 __pci_driver = {
+- .ops = &mcf3_ops,
++ .ops = &mcf3_ops_fam15h,
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = 0x1603,
+ };
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0117-mainboard-asus-kgpe-d16-Add-several-nvram-configurat.patch b/resources/libreboot/patch/kgpe-d16/0117-mainboard-asus-kgpe-d16-Add-several-nvram-configurat.patch
new file mode 100644
index 00000000..05b5bb8c
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0117-mainboard-asus-kgpe-d16-Add-several-nvram-configurat.patch
@@ -0,0 +1,127 @@
+From fa09be02971b05b8031ebc372ab778613d25da7f Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 11 Aug 2015 17:53:45 -0500
+Subject: [PATCH 117/146] mainboard/asus/kgpe-d16: Add several nvram
+ configuration options
+
+---
+ src/mainboard/asus/kgpe-d16/acpi_tables.c | 37 +++++++++++++++++++++++++++++
+ src/mainboard/asus/kgpe-d16/cmos.default | 3 ++-
+ src/mainboard/asus/kgpe-d16/cmos.layout | 7 +++---
+ src/mainboard/asus/kgpe-d16/devicetree.cb | 1 +
+ src/mainboard/asus/kgpe-d16/romstage.c | 2 ++
+ 5 files changed, 46 insertions(+), 4 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/acpi_tables.c b/src/mainboard/asus/kgpe-d16/acpi_tables.c
+index 4e98dfe..3f8650b 100644
+--- a/src/mainboard/asus/kgpe-d16/acpi_tables.c
++++ b/src/mainboard/asus/kgpe-d16/acpi_tables.c
+@@ -73,3 +73,40 @@ unsigned long acpi_fill_madt(unsigned long current)
+
+ return current;
+ }
++
++unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t* ivrs, unsigned long current)
++{
++ uint8_t *p;
++
++ uint32_t apicid_sp5100;
++ uint32_t apicid_sr5650;
++
++ apicid_sp5100 = 0x20;
++ apicid_sr5650 = apicid_sp5100 + 1;
++
++ /* Describe NB IOAPIC */
++ p = (uint8_t *)current;
++ p[0] = 0x48; /* Entry type */
++ p[1] = 0; /* Device */
++ p[2] = 0; /* Bus */
++ p[3] = 0x0; /* Data */
++ p[4] = apicid_sr5650; /* IOAPIC ID */
++ p[5] = 0x1; /* Device 0 Function 1 */
++ p[6] = 0x0; /* Northbridge bus */
++ p[7] = 0x1; /* Variety */
++ current += 8;
++
++ /* Describe SB IOAPIC */
++ p = (uint8_t *)current;
++ p[0] = 0x48; /* Entry type */
++ p[1] = 0; /* Device */
++ p[2] = 0; /* Bus */
++ p[3] = 0xd7; /* Data */
++ p[4] = apicid_sp5100; /* IOAPIC ID */
++ p[5] = 0x14 << 3; /* Device 0x14 Function 0 */
++ p[6] = 0x0; /* Southbridge bus */
++ p[7] = 0x1; /* Variety */
++ current += 8;
++
++ return current;
++}
+\ No newline at end of file
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 0a898bd..83c1fe8 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -3,7 +3,7 @@ debug_level = Spew
+ multi_core = Enable
+ slow_cpu = off
+ compute_unit_siblings = Enable
+-iommu = Disable
++iommu = Enable
+ nmi = Disable
+ hypertransport_speed_limit = Auto
+ max_mem_clock = DDR3-1600
+@@ -23,6 +23,7 @@ maximum_p_state_limit = 0xf
+ probe_filter = Auto
+ l3_cache_partitioning = Disable
+ ieee1394 = Enable
++gart = Disable
+ experimental_memory_speed_boost = Disable
+ power_on_after_fail = On
+ boot_option = Fallback
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.layout b/src/mainboard/asus/kgpe-d16/cmos.layout
+index 010d4db..310b7b1 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.layout
++++ b/src/mainboard/asus/kgpe-d16/cmos.layout
+@@ -51,9 +51,10 @@ entries
+ 473 2 e 13 dimm_spd_checksum
+ 475 1 e 14 probe_filter
+ 476 1 e 1 l3_cache_partitioning
+-477 1 e 1 experimental_memory_speed_boost
+-478 1 r 0 allow_spd_nvram_cache_restore
+-479 1 e 1 ieee1394
++477 1 e 1 ieee1394
++478 1 e 1 gart
++479 1 e 1 experimental_memory_speed_boost
++480 1 r 0 allow_spd_nvram_cache_restore
+ 728 256 h 0 user_data
+ 984 16 h 0 check_sum
+ # Reserve the extended AMD configuration registers
+diff --git a/src/mainboard/asus/kgpe-d16/devicetree.cb b/src/mainboard/asus/kgpe-d16/devicetree.cb
+index 05b975b..8879c0e 100644
+--- a/src/mainboard/asus/kgpe-d16/devicetree.cb
++++ b/src/mainboard/asus/kgpe-d16/devicetree.cb
+@@ -15,6 +15,7 @@ chip northbridge/amd/amdfam10/root_complex # Root complex
+ chip southbridge/amd/sr5650 # Primary southbridge
+ device pci 0.0 on end # HT Root Complex 0x9600
+ device pci 0.1 on end # CLKCONFIG
++ device pci 0.2 on end # IOMMU
+ device pci 2.0 on # PCIE P2P bridge 0x9603 (GPP1 Port0)
+ # Slot # PCI E 1 / PCI E 2
+ end
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 13fb485..c91fb1d 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -459,6 +459,8 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ die("After soft_reset_x - shouldn't see this message!!!\n");
+ }
+
++ sr5650_htinit_dect_and_enable_isochronous_link();
++
+ /* Set default DDR memory voltage
+ * This will be overridden later during RAM initialization
+ */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0118-southbridge-amd-sr5650-Use-correct-PCI-configuration.patch b/resources/libreboot/patch/kgpe-d16/0118-southbridge-amd-sr5650-Use-correct-PCI-configuration.patch
new file mode 100644
index 00000000..0dc6ff3d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0118-southbridge-amd-sr5650-Use-correct-PCI-configuration.patch
@@ -0,0 +1,28 @@
+From da9855ac660e4b527ca0ee754d792ea0ab361fcc Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 14 Aug 2015 02:50:44 -0500
+Subject: [PATCH 118/146] southbridge/amd/sr5650: Use correct PCI
+ configuration block offset
+
+---
+ src/southbridge/amd/sr5650/acpi/sr5650.asl | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/southbridge/amd/sr5650/acpi/sr5650.asl b/src/southbridge/amd/sr5650/acpi/sr5650.asl
+index a6ab114..1e0d5b0 100644
+--- a/src/southbridge/amd/sr5650/acpi/sr5650.asl
++++ b/src/southbridge/amd/sr5650/acpi/sr5650.asl
+@@ -19,8 +19,8 @@
+ */
+
+ Scope(\) {
+- Name(PCBA, 0xE0000000) /* Base address of PCIe config space */
+- Name(HPBA, 0xFED00000) /* Base address of HPET table */
++ Name(PCBA, CONFIG_MMCONF_BASE_ADDRESS) /* Base address of PCIe config space */
++ Name(HPBA, 0xFED00000) /* Base address of HPET table */
+
+ /* PIC IRQ mapping registers, C00h-C01h */
+ OperationRegion(PRQM, SystemIO, 0x00000C00, 0x00000002)
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0119-southbridge-amd-sr5650-Add-MCFG-ACPI-table-support.patch b/resources/libreboot/patch/kgpe-d16/0119-southbridge-amd-sr5650-Add-MCFG-ACPI-table-support.patch
new file mode 100644
index 00000000..af853d5d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0119-southbridge-amd-sr5650-Add-MCFG-ACPI-table-support.patch
@@ -0,0 +1,83 @@
+From 6eebec9d23f174057319cc373448f887e2779ddd Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 14 Aug 2015 15:20:42 -0500
+Subject: [PATCH 119/146] southbridge/amd/sr5650: Add MCFG ACPI table support
+
+---
+ src/southbridge/amd/sb700/lpc.c | 6 ------
+ src/southbridge/amd/sb800/lpc.c | 7 +------
+ src/southbridge/amd/sr5650/sr5650.c | 16 ++++++++++++++++
+ 3 files changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/lpc.c b/src/southbridge/amd/sb700/lpc.c
+index 145a01f..fc27bef 100644
+--- a/src/southbridge/amd/sb700/lpc.c
++++ b/src/southbridge/amd/sb700/lpc.c
+@@ -34,12 +34,6 @@
+ #include <cpu/amd/powernow.h>
+ #include "sb700.h"
+
+-unsigned long acpi_fill_mcfg(unsigned long current)
+-{
+- /* Just a dummy */
+- return current;
+-}
+-
+ static void lpc_init(device_t dev)
+ {
+ u8 byte;
+diff --git a/src/southbridge/amd/sb800/lpc.c b/src/southbridge/amd/sb800/lpc.c
+index 0cd5b32..af96ea7 100644
+--- a/src/southbridge/amd/sb800/lpc.c
++++ b/src/southbridge/amd/sb800/lpc.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -29,12 +30,6 @@
+ #include <arch/acpi.h>
+ #include "sb800.h"
+
+-unsigned long acpi_fill_mcfg(unsigned long current)
+-{
+- /* Just a dummy */
+- return current;
+-}
+-
+ static void lpc_init(device_t dev)
+ {
+ u8 byte;
+diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
+index b296c47..4622f36 100644
+--- a/src/southbridge/amd/sr5650/sr5650.c
++++ b/src/southbridge/amd/sr5650/sr5650.c
+@@ -800,6 +800,22 @@ static void add_ivrs_device_entries(struct device *parent, struct device *dev, i
+ free(root_level);
+ }
+
++unsigned long acpi_fill_mcfg(unsigned long current)
++{
++ struct resource *res;
++ resource_t mmconf_base = EXT_CONF_BASE_ADDRESS;
++
++ device_t dev = dev_find_slot(0, PCI_DEVFN(0, 0));
++ /* Report MMCONF base */
++ res = probe_resource(dev, 0x1c);
++ if (res)
++ mmconf_base = res->base;
++
++ current += acpi_create_mcfg_mmconfig((acpi_mcfg_mmconfig_t *)current, mmconf_base, 0x0, 0x0, 0x1f);
++
++ return current;
++}
++
+ static unsigned long acpi_fill_ivrs(acpi_ivrs_t* ivrs, unsigned long current)
+ {
+ uint8_t *p;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0120-southbridge-amd-sb700-Fix-mismatched-FADT-entries.patch b/resources/libreboot/patch/kgpe-d16/0120-southbridge-amd-sb700-Fix-mismatched-FADT-entries.patch
new file mode 100644
index 00000000..68a0a034
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0120-southbridge-amd-sb700-Fix-mismatched-FADT-entries.patch
@@ -0,0 +1,34 @@
+From 1a7a9dae89b53ec363ffbfef448db909b9e150c4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Tue, 18 Aug 2015 17:45:48 -0500
+Subject: [PATCH 120/146] southbridge/amd/sb700: Fix mismatched FADT entries
+
+---
+ src/southbridge/amd/sb700/fadt.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/fadt.c b/src/southbridge/amd/sb700/fadt.c
+index 6b1924f..209e9aa 100644
+--- a/src/southbridge/amd/sb700/fadt.c
++++ b/src/southbridge/amd/sb700/fadt.c
+@@ -131,7 +131,7 @@ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+ fadt->x_pm1b_cnt_blk.addrh = 0x0;
+
+ fadt->x_pm2_cnt_blk.space_id = 1;
+- fadt->x_pm2_cnt_blk.bit_width = 0;
++ fadt->x_pm2_cnt_blk.bit_width = 8;
+ fadt->x_pm2_cnt_blk.bit_offset = 0;
+ fadt->x_pm2_cnt_blk.resv = 0;
+ fadt->x_pm2_cnt_blk.addrl = ACPI_PMA_CNT_BLK;
+@@ -145,7 +145,7 @@ void acpi_create_fadt(acpi_fadt_t * fadt, acpi_facs_t * facs, void *dsdt)
+ fadt->x_pm_tmr_blk.addrh = 0x0;
+
+ fadt->x_gpe0_blk.space_id = 1;
+- fadt->x_gpe0_blk.bit_width = 32;
++ fadt->x_gpe0_blk.bit_width = 64;
+ fadt->x_gpe0_blk.bit_offset = 0;
+ fadt->x_gpe0_blk.resv = 0;
+ fadt->x_gpe0_blk.addrl = ACPI_GPE0_BLK;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0121-southbridge-amd-sb700-Fix-drifting-system-clock.patch b/resources/libreboot/patch/kgpe-d16/0121-southbridge-amd-sb700-Fix-drifting-system-clock.patch
new file mode 100644
index 00000000..996ac031
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0121-southbridge-amd-sb700-Fix-drifting-system-clock.patch
@@ -0,0 +1,45 @@
+From 4042f73651a2f55642357c98c0476db2a01af830 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 28 Aug 2015 15:31:31 -0500
+Subject: [PATCH 121/146] southbridge/amd/sb700: Fix drifting system clock
+
+---
+ src/southbridge/amd/sb700/early_setup.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/src/southbridge/amd/sb700/early_setup.c b/src/southbridge/amd/sb700/early_setup.c
+index 1f92a4e..f98e5c7 100644
+--- a/src/southbridge/amd/sb700/early_setup.c
++++ b/src/southbridge/amd/sb700/early_setup.c
+@@ -431,10 +431,10 @@ static void sb700_devices_por_init(void)
+
+ /* Configure HPET Counter CLK period */
+ byte = pci_read_config8(dev, 0x43);
+- byte &= 0xF7; /* unhide HPET regs */
++ byte &= 0xF7; /* Unhide HPET regs */
+ pci_write_config8(dev, 0x43, byte);
+- pci_write_config32(dev, 0x34, 0x0429B17E ); /* Counter CLK period */
+- byte |= 0x08; /* hide HPET regs */
++ pci_write_config32(dev, 0x34, 0xb0); /* HPET_CNTRL = 0xb0 */
++ byte |= 0x08; /* Hide HPET regs */
+ pci_write_config8(dev, 0x43, byte);
+
+ /* Features Enable */
+@@ -669,6 +669,14 @@ static void sb700_pmio_por_init(void)
+ byte = pmio_read(0xbb);
+ byte |= 0xc0;
+ pmio_write(0xbb, byte);
++
++#if CONFIG_SOUTHBRIDGE_AMD_SUBTYPE_SP5100
++ /* Work around system clock drift issues */
++ byte = pmio_read(0xd4);
++ byte |= 0x1 << 6; /* Enable alternate 14MHz clock source */
++ byte |= 0x1 << 7; /* Disable 25MHz oscillator buffer */
++ pmio_write(0xd4, byte);
++#endif
+ }
+
+ /*
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0122-northbridge-amd-amdmct-mct_ddr3-Add-cc6-setup-inform.patch b/resources/libreboot/patch/kgpe-d16/0122-northbridge-amd-amdmct-mct_ddr3-Add-cc6-setup-inform.patch
new file mode 100644
index 00000000..8ab3cef8
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0122-northbridge-amd-amdmct-mct_ddr3-Add-cc6-setup-inform.patch
@@ -0,0 +1,56 @@
+From ae7424bea090928c8e3ebb69882ae5a8c3f2f82e Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 20 Aug 2015 12:49:49 -0500
+Subject: [PATCH 122/146] northbridge/amd/amdmct/mct_ddr3: Add cc6 setup
+ information messages
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index b869647..e7ab88e 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1494,6 +1494,8 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ if (pMCTstat->GStatus & (1 << GSB_NodeIntlv))
+ interleaved = 1;
+
++ printk(BIOS_INFO, "%s: Initializing CC6 DRAM storage area for node %d (interleaved: %d)\n", __func__, pDCTstat->Node_ID, interleaved);
++
+ /* Find highest DRAM range (DramLimitAddr) */
+ max_node = 0;
+ max_range = -1;
+@@ -1517,6 +1519,9 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ }
+
+ if (max_range >= 0) {
++ printk(BIOS_INFO, "%s:\toriginal (node %d) max_range_limit: %16llx DRAM limit: %16llx\n", __func__, max_node, max_range_limit,
++ (((uint64_t)(Get_NB32(pDCTstat->dev_map, 0x124) & 0x1fffff)) << 27) | 0x7ffffff);
++
+ if (interleaved)
+ /* Move upper limit down by 16M * the number of nodes */
+ max_range_limit -= (0x1000000 * num_nodes);
+@@ -1524,6 +1529,8 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ /* Move upper limit down by 16M */
+ max_range_limit -= 0x1000000;
+
++ printk(BIOS_INFO, "%s:\tnew max_range_limit: %16llx\n", __func__, max_range_limit);
++
+ /* Disable the range */
+ dword = Get_NB32(pDCTstat->dev_map, 0x40 + (max_range * 0x8));
+ byte = dword & 0x3;
+@@ -1558,6 +1565,10 @@ static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+ dword &= ~(0x3f << 12); /* CoreSaveStateDestNode = destination_node */
+ dword |= (destination_node & 0x3f) << 12;
+ Set_NB32(pDCTstat->dev_link, 0x128, dword);
++
++ printk(BIOS_INFO, "%s:\tTarget node: %d\n", __func__, destination_node);
++
++ printk(BIOS_INFO, "%s:\tDone\n", __func__);
+ }
+
+ static void lock_dram_config(struct MCTStatStruc *pMCTstat,
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0123-northbridge-amd-amdfam10-Work-around-sporadic-lockup.patch b/resources/libreboot/patch/kgpe-d16/0123-northbridge-amd-amdfam10-Work-around-sporadic-lockup.patch
new file mode 100644
index 00000000..98bf95a4
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0123-northbridge-amd-amdfam10-Work-around-sporadic-lockup.patch
@@ -0,0 +1,38 @@
+From e3c6eaf075a7991c4723e386fb215532fd9af4d0 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 20 Aug 2015 15:53:25 -0500
+Subject: [PATCH 123/146] northbridge/amd/amdfam10: Work around sporadic
+ lockups when CC6 enabled
+
+---
+ src/northbridge/amd/amdfam10/northbridge.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index e374ed4..a8d9ce6 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -812,6 +812,20 @@ static void amdfam10_domain_read_resources(device_t dev)
+ else
+ qword = 0x1000000;
+
++ /* FIXME
++ * The BKDG appears to be incorrect as to the location of the CC6 save region
++ * lower boundary on non-interleaved systems, causing lockups on attempted write
++ * to the CC6 save region.
++ *
++ * For now, work around by allocating the maximum possible CC6 save region size.
++ *
++ * Determine if this is a BKDG error or a setup problem and remove this warning!
++ */
++ qword = (0x1 << 27);
++ max_range_limit = (((uint64_t)(pci_read_config32(get_node_pci(max_node, 1), 0x124) & 0x1fffff)) << 27) - 1;
++
++ printk(BIOS_INFO, "Reserving CC6 save segment base: %08llx size: %08llx\n", (max_range_limit + 1), qword);
++
+ /* Reserve the CC6 save segment */
+ reserved_ram_resource(dev, 8, (max_range_limit + 1) >> 10, qword >> 10);
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0124-northbridge-amd-amdmct-mct_ddr3-Ensure-channel-clock.patch b/resources/libreboot/patch/kgpe-d16/0124-northbridge-amd-amdmct-mct_ddr3-Ensure-channel-clock.patch
new file mode 100644
index 00000000..aa99c41d
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0124-northbridge-amd-amdmct-mct_ddr3-Ensure-channel-clock.patch
@@ -0,0 +1,113 @@
+From a3fb888625af595dda6c2ee181f329259a12cc78 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 13:17:14 -0500
+Subject: [PATCH 124/146] northbridge/amd/amdmct/mct_ddr3: Ensure channel
+ clock skew is properly set up
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 46 +++++++++++++++------------
+ 1 file changed, 25 insertions(+), 21 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index e7ab88e..78a5255 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -3042,6 +3042,23 @@ static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTst
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
+ if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n");
++
++ /* SkewMemClk must be set before MemClkFreqVal is set
++ * This relies on DCTInit_D being called for DCT 1 after
++ * it has already been called for DCT 0...
++ */
++ if (is_fam15h()) {
++ /* Set memory clock skew if needed */
++ if (dct == 1) {
++ if (!pDCTstat->stopDCT[0]) {
++ printk(BIOS_DEBUG, "\t\tDCTInit_D: enabling intra-channel clock skew\n");
++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe00a);
++ dword |= (0x1 << 4); /* SkewMemClk = 1 */
++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe00a, dword);
++ }
++ }
++ }
++
+ if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
+ if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+@@ -3051,8 +3068,6 @@ static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTst
+ }
+ }
+ }
+-
+-
+ }
+
+ static void DCTFinalInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
+@@ -3060,17 +3075,6 @@ static void DCTFinalInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *p
+ uint32_t dword;
+
+ /* Finalize DRAM init on a single node */
+- if (is_fam15h()) {
+- /* Set memory clock skew if needed */
+- if (dct == 0) {
+- if (!pDCTstat->stopDCT[0] && !pDCTstat->stopDCT[1]) {
+- dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0d0fe00a);
+- dword |= (0x1 << 4); /* SkewMemClk = 1 */
+- Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0d0fe00a, dword);
+- }
+- }
+- }
+-
+ if (!pDCTstat->stopDCT[dct]) {
+ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
+ printk(BIOS_DEBUG, "\t\tDCTFinalInit_D: StartupDCT_D Start\n");
+@@ -3273,28 +3277,28 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ if (Twtr < val)
+ Twtr = val;
+
+- val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xFF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xff;
+ val >>= 4;
+ val <<= 8;
+- val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCmin] & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRCmin] & 0xff;
+ val *= MTB16x;
+ if (Trc < val)
+ Trc = val;
+
+- byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Density] & 0xF;
++ byte = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Density] & 0xf;
+ if (Trfc[LDIMM] < byte)
+ Trfc[LDIMM] = byte;
+
+- val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tRAS_tRC] & 0xf;
+ val <<= 8;
+- val |= (pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRASmin] & 0xFF);
++ val |= (pDCTstat->spd_data.spd_bytes[dct + i][SPD_tRASmin] & 0xff);
+ val *= MTB16x;
+ if (Tras < val)
+ Tras = val;
+
+- val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tFAW] & 0xF;
++ val = pDCTstat->spd_data.spd_bytes[dct + i][SPD_Upper_tFAW] & 0xf;
+ val <<= 8;
+- val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tFAWmin] & 0xFF;
++ val |= pDCTstat->spd_data.spd_bytes[dct + i][SPD_tFAWmin] & 0xff;
+ val *= MTB16x;
+ if (Tfaw < val)
+ Tfaw = val;
+@@ -3501,7 +3505,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ /* Trfc0-Trfc3 */
+ for (i=0; i<4; i++)
+ if (pDCTstat->Trfc[i] == 0x0)
+- pDCTstat->Trfc[i] = 0x4;
++ pDCTstat->Trfc[i] = 0x1;
+ dword = Get_NB32_DCT(dev, dct, 0x208); /* DRAM Timing 2 */
+ dword &= ~(0x07070707);
+ dword |= (pDCTstat->Trfc[3] & 0x7) << 24; /* Trfc3 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0125-northbridge-amd-amdmct-mct_ddr3-Add-DDR3-termination.patch b/resources/libreboot/patch/kgpe-d16/0125-northbridge-amd-amdmct-mct_ddr3-Add-DDR3-termination.patch
new file mode 100644
index 00000000..4dd56051
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0125-northbridge-amd-amdmct-mct_ddr3-Add-DDR3-termination.patch
@@ -0,0 +1,34 @@
+From 2255079d463f0a09f74a8d0d64208772de5e0bc5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 13:18:06 -0500
+Subject: [PATCH 125/146] northbridge/amd/amdmct/mct_ddr3: Add DDR3
+ termination debug output
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 09a5f68..7804a38 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -163,6 +163,8 @@ static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t d
+ }
+ }
+
++ printk(BIOS_INFO, "DIMM %d RttWr: %01x\n", dimm, term);
++
+ return term;
+ }
+
+@@ -362,6 +364,7 @@ static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t
+ }
+ }
+
++ printk(BIOS_INFO, "DIMM %d RttNom: %01x\n", dimm, term);
+ return term;
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0126-northbridge-amd-amdmct-mct_ddr3-Fix-a-minor-RDIMM-CS.patch b/resources/libreboot/patch/kgpe-d16/0126-northbridge-amd-amdmct-mct_ddr3-Fix-a-minor-RDIMM-CS.patch
new file mode 100644
index 00000000..d6788b5c
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0126-northbridge-amd-amdmct-mct_ddr3-Fix-a-minor-RDIMM-CS.patch
@@ -0,0 +1,35 @@
+From 85a652d6f36b200437c86b84cc76304271c14a1e Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 13:18:53 -0500
+Subject: [PATCH 126/146] northbridge/amd/amdmct/mct_ddr3: Fix a minor RDIMM
+ CS select error
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+index 624a543..8fd2523 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+@@ -236,7 +236,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ val = Get_NB32_DCT(dev, dct, 0xa8);
+- val &= ~(0xf << 8);
++ val &= ~(0xff << 8);
+
+ switch (MrsChipSel) {
+ case 0:
+@@ -283,7 +283,7 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
+ val = Get_NB32_DCT(dev, dct, 0xa8);
+ val &= ~(0xff << 8);
+- val |= (0x3 << (MrsChipSel & 0xfe)) << 8;
++ val |= (0x3 << (MrsChipSel & ~0x1)) << 8;
+ Set_NB32_DCT(dev, dct, 0xa8, val);
+
+ /* Resend control word 10 */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0127-northbridge-amd-amdmct-mct_ddr3-Fix-odd-rank-data-co.patch b/resources/libreboot/patch/kgpe-d16/0127-northbridge-amd-amdmct-mct_ddr3-Fix-odd-rank-data-co.patch
new file mode 100644
index 00000000..f0af7290
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0127-northbridge-amd-amdmct-mct_ddr3-Fix-odd-rank-data-co.patch
@@ -0,0 +1,63 @@
+From a89e50c7cef4090f00f530da8161d7d63d111e82 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 13:19:34 -0500
+Subject: [PATCH 127/146] northbridge/amd/amdmct/mct_ddr3: Fix odd rank data
+ corruption due to incorrect DQS training
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 25 ++++++++++++++++--------
+ 1 file changed, 17 insertions(+), 8 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index eedff67..98d2f11 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -1324,9 +1324,9 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ Receiver = receiver_start;
+
+ /* There are four receiver pairs, loosely associated with chipselects.
+- * This is essentially looping over each DIMM.
++ * This is essentially looping over each rank within each DIMM.
+ */
+- for (; Receiver < receiver_end; Receiver += 2) {
++ for (; Receiver < receiver_end; Receiver++) {
+ dimm = (Receiver >> 1);
+ if ((Receiver & 0x1) == 0) {
+ /* Even rank of DIMM */
+@@ -1340,18 +1340,27 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ continue;
+ }
+
++#if DQS_TRAIN_DEBUG > 0
++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: Training DQS read/write position for receiver %d (DIMM %d)\n", Receiver, dimm);
++#endif
++
+ /* Initialize variables */
+ for (lane = lane_start; lane < lane_end; lane++) {
+ passing_dqs_delay_found[lane] = 0;
+ }
+- memset(dqs_results_array, 0, sizeof(dqs_results_array));
++ if ((Receiver & 0x1) == 0) {
++ /* Even rank of DIMM */
++ memset(dqs_results_array, 0, sizeof(dqs_results_array));
++
++ /* Read initial read / write DQS delays */
++ read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg);
++ read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
+
+- /* Read initial read / write DQS delays */
+- read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg);
+- read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
++ /* Read current settings of other (previously trained) lanes */
++ read_dqs_write_data_timing_registers(initial_write_data_timing, dev, dct, dimm, index_reg);
++ }
+
+- /* Read current settings of other (previously trained) lanes */
+- read_dqs_write_data_timing_registers(initial_write_data_timing, dev, dct, dimm, index_reg);
++ /* Initialize iterators */
+ memcpy(current_write_data_delay, initial_write_data_timing, sizeof(current_write_data_delay));
+
+ for (lane = lane_start; lane < lane_end; lane++) {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0128-northbridge-amd-amdmct-mct_ddr3-Use-antiphase-to-bet.patch b/resources/libreboot/patch/kgpe-d16/0128-northbridge-amd-amdmct-mct_ddr3-Use-antiphase-to-bet.patch
new file mode 100644
index 00000000..6edf3835
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0128-northbridge-amd-amdmct-mct_ddr3-Use-antiphase-to-bet.patch
@@ -0,0 +1,156 @@
+From 246b8e4f7eb96fac6aa7f4c3133e48b30ed2637f Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 15:10:19 -0500
+Subject: [PATCH 128/146] northbridge/amd/amdmct/mct_ddr3: Use antiphase to
+ better center DQS window
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 59 ++++++++++++++++--------
+ 1 file changed, 41 insertions(+), 18 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 98d2f11..fa5a124 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -1304,7 +1304,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
+ uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
+ uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
+- uint8_t dqs_results_array[2][(lane_end - lane_start)][32][32]; /* [rank][lane][write step][read step] */
++ uint8_t dqs_results_array[2][(lane_end - lane_start)][32][48]; /* [rank][lane][write step][read step + 16] */
+
+ uint8_t last_pos = 0;
+ uint8_t cur_count = 0;
+@@ -1404,16 +1404,24 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ */
+ dword = Get_NB32_DCT(dev, dct, 0x268) & 0x3ffff;
+ if (dword & (0x3 << (lane * 2)))
+- dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 0; /* Fail */
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][(current_read_dqs_delay[lane] >> 1) + 16] = 0; /* Fail */
+ else
+- dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 1; /* Pass */
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][(current_read_dqs_delay[lane] >> 1) + 16] = 1; /* Pass */
++ if ((current_read_dqs_delay[lane] >> 1) >= (32 - 16)) {
++ /* Check antiphase results */
++ dword = Get_NB32_DCT(dev, dct, 0x26c) & 0x3ffff;
++ if (dword & (0x3 << (lane * 2)))
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][16 - (32 - (current_read_dqs_delay[lane] >> 1))] = 0; /* Fail */
++ else
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][16 - (32 - (current_read_dqs_delay[lane] >> 1))] = 1; /* Pass */
++ }
+ }
+ }
+
+ if (dual_rank && (Receiver & 0x1)) {
+ /* Overlay the previous rank test results with the current rank */
+ for (write_iter = 0; write_iter < 32; write_iter++) {
+- for (read_iter = 0; read_iter < 32; read_iter++) {
++ for (read_iter = 0; read_iter < 48; read_iter++) {
+ if ((dqs_results_array[0][lane - lane_start][write_iter][read_iter])
+ && (dqs_results_array[1][lane - lane_start][write_iter][read_iter]))
+ dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 1;
+@@ -1431,8 +1439,8 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ best_pos = 0;
+ best_count = 0;
+ for (write_iter = 0; write_iter < 32; write_iter++) {
+- for (read_iter = 0; read_iter < 32; read_iter++) {
+- if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (read_iter < 31)) {
++ for (read_iter = 0; read_iter < 48; read_iter++) {
++ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (read_iter < 47)) {
+ /* Pass */
+ cur_count++;
+ } else {
+@@ -1442,18 +1450,28 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ best_pos = last_pos;
+ }
+ cur_count = 0;
+- last_pos = read_iter;
++ last_pos = read_iter + 1;
+ }
+ }
+ last_pos = 0;
+ }
+-
++
+ if (best_count > 2) {
++ uint16_t region_center = (best_pos + (best_count / 2));
++
++ if (region_center < 16) {
++ printk(BIOS_WARNING, "TrainDQSRdWrPos: negative DQS recovery delay detected!"
++ " Attempting to continue but your system may be unstable...\n");
++ region_center = 0;
++ } else {
++ region_center -= 16;
++ }
++
+ /* Restore current settings of other (previously trained) lanes to the active array */
+ memcpy(current_read_dqs_delay, initial_read_dqs_delay, sizeof(current_read_dqs_delay));
+
+ /* Program the Read DQS Timing Control register with the center of the passing window */
+- current_read_dqs_delay[lane] = ((best_pos << 1) + ((best_count << 1) / 2));
++ current_read_dqs_delay[lane] = region_center << 1;
+ passing_dqs_delay_found[lane] = 1;
+
+ /* Commit the current Read DQS Timing Control settings to the hardware registers */
+@@ -1464,6 +1482,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+
+ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest read passing region ", best_count, 4);
+ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest read passing region start ", best_pos, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest read passing region center (raw hardware value) ", region_center, 4);
+ } else {
+ /* Reprogram the Read DQS Timing Control register with the original settings */
+ write_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
+@@ -1476,7 +1495,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ cur_count = 0;
+ best_pos = 0;
+ best_count = 0;
+- for (read_iter = 0; read_iter < 32; read_iter++) {
++ for (read_iter = 0; read_iter < 48; read_iter++) {
+ for (write_iter = 0; write_iter < 32; write_iter++) {
+ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (write_iter < 31)) {
+ /* Pass */
+@@ -1488,7 +1507,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ best_pos = last_pos;
+ }
+ cur_count = 0;
+- last_pos = write_iter;
++ last_pos = write_iter + 1;
+ }
+ }
+ last_pos = 0;
+@@ -1511,8 +1530,8 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ /* Save the final Write Data Timing settings for later use */
+ pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
+
+- print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest write passing region ", best_count, 4);
+- print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region start ", best_pos, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region ", best_count, 4);
++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 146 largest write passing region start ", best_pos, 4);
+ } else {
+ /* Reprogram the Write DQS Timing Control register with the original settings */
+ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg);
+@@ -1521,12 +1540,16 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+
+ #ifdef PRINT_PASS_FAIL_BITMAPS
+ for (lane = lane_start; lane < lane_end; lane++) {
+- for (read_iter = 0; read_iter < 32; read_iter++) {
+- for (write_iter = 0; write_iter < 32; write_iter++) {
+- if (dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter])
++ for (write_iter = 0; write_iter < 32; write_iter++) {
++ for (read_iter = 0; read_iter < 48; read_iter++) {
++ if (dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) {
+ printk(BIOS_DEBUG, "+");
+- else
+- printk(BIOS_DEBUG, ".");
++ } else {
++ if (read_iter < 16)
++ printk(BIOS_DEBUG, "°");
++ else
++ printk(BIOS_DEBUG, ".");
++ }
+ }
+ printk(BIOS_DEBUG, "\n");
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0129-northbridge-amd-amdmct-mct_ddr3-Fix-broken-support-f.patch b/resources/libreboot/patch/kgpe-d16/0129-northbridge-amd-amdmct-mct_ddr3-Fix-broken-support-f.patch
new file mode 100644
index 00000000..febfb9a9
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0129-northbridge-amd-amdmct-mct_ddr3-Fix-broken-support-f.patch
@@ -0,0 +1,602 @@
+From 36ab0ce442a7394eacffd4770e4c159491414be8 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 27 Aug 2015 23:37:38 -0500
+Subject: [PATCH 129/146] northbridge/amd/amdmct/mct_ddr3: Fix broken support
+ for multiple DIMMs on single channel
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 340 +++++++++++++++++++-----
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 8 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 2 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 26 +-
+ src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 4 +
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 8 +-
+ 6 files changed, 312 insertions(+), 76 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 78a5255..8564fed 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1360,6 +1360,224 @@ static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dc
+ return slow_access;
+ }
+
++static uint8_t fam15h_odt_tristate_enable_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
++
++ uint8_t package_type;
++ uint8_t odt_tristate_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 104 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 == 1)
++ odt_tristate_code = 0xe;
++ else
++ odt_tristate_code = 0xa;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm1 == 1)
++ odt_tristate_code = 0xd;
++ else
++ odt_tristate_code = 0x5;
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ odt_tristate_code = 0xc;
++ else if ((rank_count_dimm0 == 1) && (rank_count_dimm1 >= 2))
++ odt_tristate_code = 0x4;
++ else if ((rank_count_dimm0 >= 2) && (rank_count_dimm1 == 1))
++ odt_tristate_code = 0x8;
++ else
++ odt_tristate_code = 0x0;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++
++ /* TODO
++ * Implement LRDIMM support
++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 105
++ */
++ } else {
++ /* UDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 103 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 == 1)
++ odt_tristate_code = 0xe;
++ else
++ odt_tristate_code = 0xa;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 == 1)
++ odt_tristate_code = 0xd;
++ else
++ odt_tristate_code = 0x5;
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ odt_tristate_code = 0xc;
++ else if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 2))
++ odt_tristate_code = 0x4;
++ else if ((rank_count_dimm0 == 2) && (rank_count_dimm1 == 1))
++ odt_tristate_code = 0x8;
++ else
++ odt_tristate_code = 0x0;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return odt_tristate_code;
++}
++
++static uint8_t fam15h_cs_tristate_enable_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++{
++ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
++
++ uint8_t package_type;
++ uint8_t cs_tristate_code = 0;
++
++ package_type = mctGet_NVbits(NV_PACK_TYPE);
++
++ /* Obtain number of DIMMs on channel */
++ uint8_t dimm_count = pDCTstat->MAdimms[dct];
++ uint8_t rank_count_dimm0;
++ uint8_t rank_count_dimm1;
++
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ /* RDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 104 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 < 4)
++ cs_tristate_code = 0xfc;
++ else
++ cs_tristate_code = 0xcc;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm1 < 4)
++ cs_tristate_code = 0xf3;
++ else
++ cs_tristate_code = 0x33;
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4))
++ cs_tristate_code = 0xf0;
++ else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4))
++ cs_tristate_code = 0x30;
++ else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4))
++ cs_tristate_code = 0xc0;
++ else
++ cs_tristate_code = 0x0;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
++ /* LRDIMM */
++
++ /* TODO
++ * Implement LRDIMM support
++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 105
++ */
++ } else {
++ /* UDIMM */
++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.10.1 Table 103 */
++ if (MaxDimmsInstallable == 1) {
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 == 1)
++ cs_tristate_code = 0xfe;
++ else
++ cs_tristate_code = 0xfc;
++ } else if (MaxDimmsInstallable == 2) {
++ if (dimm_count == 1) {
++ /* 1 DIMM detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if (rank_count_dimm0 == 1)
++ cs_tristate_code = 0xfb;
++ else
++ cs_tristate_code = 0xf3;
++ } else if (dimm_count == 2) {
++ /* 2 DIMMs detected */
++ rank_count_dimm0 = pDCTstat->DimmRanks[(0 * 2) + dct];
++ rank_count_dimm1 = pDCTstat->DimmRanks[(1 * 2) + dct];
++
++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
++ cs_tristate_code = 0xfa;
++ else if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 2))
++ cs_tristate_code = 0xf2;
++ else if ((rank_count_dimm0 == 2) && (rank_count_dimm1 == 1))
++ cs_tristate_code = 0xf8;
++ else
++ cs_tristate_code = 0xf0;
++ }
++ } else if (MaxDimmsInstallable == 3) {
++ /* TODO
++ * 3 DIMM/channel support unimplemented
++ */
++ }
++ }
++ } else {
++ /* TODO
++ * Other socket support unimplemented
++ */
++ }
++
++ return cs_tristate_code;
++}
++
+ static void set_2t_configuration(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+ {
+@@ -2294,20 +2512,16 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ if (memclk_index <= 0x6) {
+ delay = 0x5;
+ delay2 = 0x3;
+- }
+- else if (memclk_index == 0xa) {
++ } else if (memclk_index == 0xa) {
+ delay = 0x6;
+ delay2 = 0x3;
+- }
+- else if (memclk_index == 0xe) {
++ } else if (memclk_index == 0xe) {
+ delay = 0x7;
+ delay2 = 0x4;
+- }
+- else if (memclk_index == 0x12) {
++ } else if (memclk_index == 0x12) {
+ delay = 0x8;
+ delay2 = 0x4;
+- }
+- else if (memclk_index == 0x16) {
++ } else if (memclk_index == 0x16) {
+ delay = 0xa;
+ delay2 = 0x5;
+ }
+@@ -3320,8 +3534,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ tCK16x = 40;
+ else
+ tCK16x = 48;
+- }
+- else {
++ } else {
+ if (byte == 7)
+ tCK16x = 20;
+ else if (byte == 6)
+@@ -4648,13 +4861,13 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ else
+ pDCTstat->RegMan1Present |= 1 << i;
+ }
+- /* Get Control word values for RC3. We dont need it. */
++ /* Get control word value for RC3 */
+ byte = pDCTstat->spd_data.spd_bytes[i][70];
+- pDCTstat->CtrlWrd3 |= (byte >> 4) << (i << 2); /* C3 = SPD byte 70 [7:4] */
+- /* Get Control word values for RC4, and RC5 */
++ pDCTstat->CtrlWrd3 |= ((byte >> 4) & 0xf) << (i << 2); /* RC3 = SPD byte 70 [7:4] */
++ /* Get control word values for RC4 and RC5 */
+ byte = pDCTstat->spd_data.spd_bytes[i][71];
+- pDCTstat->CtrlWrd4 |= (byte & 0xFF) << (i << 2); /* RC4 = SPD byte 71 [3:0] */
+- pDCTstat->CtrlWrd5 |= (byte >> 4) << (i << 2); /* RC5 = SPD byte 71 [7:4] */
++ pDCTstat->CtrlWrd4 |= (byte & 0xf) << (i << 2); /* RC4 = SPD byte 71 [3:0] */
++ pDCTstat->CtrlWrd5 |= ((byte >> 4) & 0xf) << (i << 2); /* RC5 = SPD byte 71 [7:4] */
+ }
+ }
+ }
+@@ -5836,23 +6049,27 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat,
+ u32 val;
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg = 0x98;
+- u32 index;
+ u16 word;
+
+- /* Tri-state unused chipselects when motherboard
+- termination is available */
+-
+- /* FIXME: skip for Ax */
+-
+- word = pDCTstat->CSPresent;
+- if (pDCTstat->Status & (1 << SB_Registered)) {
+- word |= (word & 0x55) << 1;
++ if (is_fam15h()) {
++ word = fam15h_cs_tristate_enable_code(pDCTstat, dct);
++ } else {
++ /* Tri-state unused chipselects when motherboard
++ termination is available */
++
++ /* FIXME: skip for Ax */
++
++ word = pDCTstat->CSPresent;
++ if (pDCTstat->Status & (1 << SB_Registered)) {
++ word |= (word & 0x55) << 1;
++ }
++ word = (~word) & 0xff;
+ }
+- word = (~word) & 0xFF;
+- index = 0x0c;
+- val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ val &= ~0xff;
+ val |= word;
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, val);
+ }
+
+ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+@@ -5861,7 +6078,6 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ u32 val;
+ u32 dev;
+ u32 index_reg = 0x98;
+- u32 index;
+ u16 word;
+
+ /* Tri-state unused CKEs when motherboard termination is available */
+@@ -5871,15 +6087,13 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ dev = pDCTstat->dev_dct;
+ word = pDCTstat->CSPresent;
+
+- index = 0x0c;
+- val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ val &= ~(0x3 << 12);
+ if ((word & 0x55) == 0)
+ val |= 1 << 12;
+-
+- if ((word & 0xAA) == 0)
++ if ((word & 0xaa) == 0)
+ val |= 1 << 13;
+-
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, val);
+ }
+
+ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+@@ -5889,42 +6103,44 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+ u32 dev;
+ u32 index_reg = 0x98;
+ u8 cs;
+- u32 index;
+ u8 odt;
+ u8 max_dimms;
+
+- /* FIXME: skip for Ax */
+-
+ dev = pDCTstat->dev_dct;
+
+- /* Tri-state unused ODTs when motherboard termination is available */
+- max_dimms = (u8) mctGet_NVbits(NV_MAX_DIMMS);
+- odt = 0x0F; /* ODT tri-state setting */
+-
+- if (pDCTstat->Status & (1 <<SB_Registered)) {
+- for (cs = 0; cs < 8; cs += 2) {
+- if (pDCTstat->CSPresent & (1 << cs)) {
+- odt &= ~(1 << (cs / 2));
+- if (mctGet_NVbits(NV_4RANKType) != 0) { /* quad-rank capable platform */
+- if (pDCTstat->CSPresent & (1 << (cs + 1)))
+- odt &= ~(4 << (cs / 2));
++ if (is_fam15h()) {
++ odt = fam15h_odt_tristate_enable_code(pDCTstat, dct);
++ } else {
++ /* FIXME: skip for Ax */
++
++ /* Tri-state unused ODTs when motherboard termination is available */
++ max_dimms = (u8) mctGet_NVbits(NV_MAX_DIMMS);
++ odt = 0x0f; /* ODT tri-state setting */
++
++ if (pDCTstat->Status & (1 <<SB_Registered)) {
++ for (cs = 0; cs < 8; cs += 2) {
++ if (pDCTstat->CSPresent & (1 << cs)) {
++ odt &= ~(1 << (cs / 2));
++ if (mctGet_NVbits(NV_4RANKType) != 0) { /* quad-rank capable platform */
++ if (pDCTstat->CSPresent & (1 << (cs + 1)))
++ odt &= ~(4 << (cs / 2));
++ }
+ }
+ }
++ } else { /* AM3 package */
++ val = ~(pDCTstat->CSPresent);
++ odt = val & 9; /* swap bits 1 and 2 */
++ if (val & (1 << 1))
++ odt |= 1 << 2;
++ if (val & (1 << 2))
++ odt |= 1 << 1;
+ }
+- } else { /* AM3 package */
+- val = ~(pDCTstat->CSPresent);
+- odt = val & 9; /* swap bits 1 and 2 */
+- if (val & (1 << 1))
+- odt |= 1 << 2;
+- if (val & (1 << 2))
+- odt |= 1 << 1;
+ }
+
+- index = 0x0C;
+- val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+- val |= ((odt & 0xFF) << 8); /* set bits 11:8 ODTTriState[3:0] */
+- Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+-
++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
++ val &= ~(0xf << 8); /* ODTTri = odt */
++ val |= (odt & 0xf) << 8;
++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, val);
+ }
+
+ /* Family 15h */
+@@ -6494,7 +6710,7 @@ static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+ dword |= (read_odt_delay & 0xf);
+ Set_NB32_DCT(dev, dct, 0x240, dword);
+
+- printk(BIOS_SPEW, "Programmed ODT pattern %08x %08x %08x %08x\n", odt_pattern_0, odt_pattern_1, odt_pattern_2, odt_pattern_3);
++ printk(BIOS_SPEW, "Programmed DCT %d ODT pattern %08x %08x %08x %08x\n", dct, odt_pattern_0, odt_pattern_1, odt_pattern_2, odt_pattern_3);
+ } else if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+ if (pDCTstat->Speed == 3)
+ dword = 0x00000800;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+index ec5658e..8bc4ec2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+@@ -582,7 +582,7 @@ struct DCTStatStruc { /* A per Node structure*/
+ uint8_t NbPstateThreshold;
+ uint8_t NbPstateHi;
+
+-/* New for LB Support */
++ /* New for LB Support */
+ u8 NodePresent;
+ u32 dev_host;
+ u32 dev_map;
+@@ -592,9 +592,9 @@ struct DCTStatStruc { /* A per Node structure*/
+ u32 dev_nbctl;
+ u8 TargetFreq;
+ u8 TargetCASL;
+- u8 CtrlWrd3;
+- u8 CtrlWrd4;
+- u8 CtrlWrd5;
++ uint32_t CtrlWrd3;
++ uint32_t CtrlWrd4;
++ uint32_t CtrlWrd5;
+ u8 DqsRdWrPos_Saved;
+ u8 DqsRcvEnGrossMax;
+ u8 DqsRcvEnGrossMin;
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index fa5a124..9c9a8c2 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -1021,7 +1021,7 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+ pDCTstat->CH_MaxRdLat[dct] = n - 1;
+
+ #if DQS_TRAIN_DEBUG > 0
+- printk(BIOS_DEBUG, "%s: CH_MaxRdLat[%d]: %03x\n", __func__, dct, pDCTstat->CH_MaxRdLat[dct]);
++ printk(BIOS_DEBUG, "%s: CH_MaxRdLat[%d]: %03x\n", __func__, dct, pDCTstat->CH_MaxRdLat[dct]);
+ #endif
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+index 8fd2523..dec2bf8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+@@ -18,7 +18,7 @@
+ * Foundation, Inc.
+ */
+
+-static uint8_t fam15h_rdimm_rc2_control_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
++static uint8_t fam15h_rdimm_rc2_ibt_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+ {
+ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+@@ -161,7 +161,7 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ val = 0xc; /* if single rank, set DBA1 and DBA0 */
+ } else if (CtrlWordNum == 2) {
+ if (is_fam15h()) {
+- val = fam15h_rdimm_rc2_control_code(pDCTstat, dct);
++ val = (fam15h_rdimm_rc2_ibt_code(pDCTstat, dct) & 0x1) << 2;
+ } else {
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+@@ -178,10 +178,14 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ } else if (CtrlWordNum == 5) {
+ val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xff;
+ } else if (CtrlWordNum == 8) {
+- if (package_type == PT_GR) {
+- /* Socket G34 */
+- if (MaxDimmsInstallable == 2) {
+- val = 0x0;
++ if (is_fam15h()) {
++ val = (fam15h_rdimm_rc2_ibt_code(pDCTstat, dct) & 0xe) >> 1;
++ } else {
++ if (package_type == PT_GR) {
++ /* Socket G34 */
++ if (MaxDimmsInstallable == 2) {
++ val = 0x0;
++ }
+ }
+ }
+ } else if (CtrlWordNum == 9) {
+@@ -233,7 +237,11 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+
+ mct_Wait(1200);
+
+- for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
++ if (pDCTstat->GangedMode & 1)
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
++
++ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel += 2) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ val = Get_NB32_DCT(dev, dct, 0xa8);
+ val &= ~(0xff << 8);
+@@ -276,6 +284,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ u32 val;
+ uint16_t mem_freq;
+
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
++ if (pDCTstat->GangedMode & 1)
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
++
+ pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
+ mem_freq = memclk_to_freq(pDCTstat->TargetFreq);
+ for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+index 7804a38..5019faa 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+@@ -845,6 +845,10 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ */
+ }
+
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
++ if (pDCTstat->GangedMode & 1)
++ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
++
+ /* The following steps are performed once for unbuffered DIMMs and once for each
+ * chip select on registered DIMMs: */
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel++) {
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 3391dcf..81a0e69 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -925,7 +925,7 @@ void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+-void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm)
++void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm)
+ {
+ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+@@ -933,6 +933,10 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ u8 WrLvOdt1=0;
+
+ if (is_fam15h()) {
++ /* On Family15h processors, the value for the specific CS being targetted
++ * is taken from F2x238 / F2x23C as appropriate, then loaded into F2x9C_x0000_0008
++ */
++
+ /* Convert DIMM number to CS */
+ uint32_t dword;
+ uint8_t cs;
+@@ -967,7 +971,7 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
+
+- printk(BIOS_SPEW, "Programmed DCT %d write levelling ODT pattern %08x\n", dct, WrLvOdt1);
++ printk(BIOS_SPEW, "Programmed DCT %d write levelling ODT pattern %08x from DIMM %d data\n", dct, WrLvOdt1, dimm);
+
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0130-drivers-pc80-Add-optional-spinlock-for-nvram-CBFS-ac.patch b/resources/libreboot/patch/kgpe-d16/0130-drivers-pc80-Add-optional-spinlock-for-nvram-CBFS-ac.patch
new file mode 100644
index 00000000..903204c0
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0130-drivers-pc80-Add-optional-spinlock-for-nvram-CBFS-ac.patch
@@ -0,0 +1,172 @@
+From e6d5c8b5568e0fd6c149c7202d2084c7451875c5 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 28 Aug 2015 19:52:05 -0500
+Subject: [PATCH 130/146] drivers/pc80: Add optional spinlock for nvram CBFS
+ access
+
+When enabling the IOMMU on certain systems dmesg is spammed with I/O page faults like the following:
+AMD-Vi: Event logged [IO_PAGE_FAULT device=00:14.0 domain=0x000a address=0x000000fdf9103300 flags=0x0030]
+
+Decoding the faulting address:
+0x000000fdf9103300
+ fdf91x Hypertransport system management region
+ 33 SysMgtCmd (System Management Command) = 0x33
+ 3 Base Command Type = 0x3: STPCLK (Stop Clock request)
+ 3 SMAF (System Management Action Field) = [3:1] = 0x1
+ 1 Signal State Bit Map = [0] = 0x1
+
+Therefore, the error appears to be triggered by an upstream C1E request.
+
+This was eventually traced to concurrent access to the SP5100's SMBus controller by
+multiple APs during startup. Calls to the nvram read functions get_option and read_option
+call CBFS functions, which in turn make near-simultaneous requests to the SMBus controller,
+thus placing the SP5100 in an invalid state. This limitation is not documented in any public
+AMD errata, and was only discovered through considerable debugging effort.
+---
+ src/Kconfig | 4 +++
+ src/arch/x86/include/arch/smp/spinlock.h | 4 ++-
+ src/cpu/amd/car/post_cache_as_ram.c | 3 +++
+ src/drivers/pc80/mc146818rtc.c | 43 ++++++++++++++++++++++++++++--
+ 4 files changed, 51 insertions(+), 3 deletions(-)
+
+diff --git a/src/Kconfig b/src/Kconfig
+index 2e6b5bc..f1b7ebe 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -451,6 +451,10 @@ config HAVE_ROMSTAGE_CONSOLE_SPINLOCK
+ bool
+ default n
+
++config HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK
++ bool
++ default n
++
+ config HAVE_MONOTONIC_TIMER
+ def_bool n
+ help
+diff --git a/src/arch/x86/include/arch/smp/spinlock.h b/src/arch/x86/include/arch/smp/spinlock.h
+index 5000779..cf142a9 100644
+--- a/src/arch/x86/include/arch/smp/spinlock.h
++++ b/src/arch/x86/include/arch/smp/spinlock.h
+@@ -1,7 +1,7 @@
+ #ifndef ARCH_SMP_SPINLOCK_H
+ #define ARCH_SMP_SPINLOCK_H
+
+-#if !defined(__PRE_RAM__) || defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
++#if !defined(__PRE_RAM__) || defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK) || defined(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
+
+ /*
+ * Your basic SMP spinlocks, allowing only a single CPU anywhere
+@@ -14,6 +14,8 @@ typedef struct {
+ #ifdef __PRE_RAM__
+ spinlock_t* romstage_console_lock(void);
+ void initialize_romstage_console_lock(void);
++spinlock_t* romstage_nvram_cbfs_lock(void);
++void initialize_romstage_nvram_cbfs_lock(void);
+ #endif
+
+ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 }
+diff --git a/src/cpu/amd/car/post_cache_as_ram.c b/src/cpu/amd/car/post_cache_as_ram.c
+index 503a666..291f4f5 100644
+--- a/src/cpu/amd/car/post_cache_as_ram.c
++++ b/src/cpu/amd/car/post_cache_as_ram.c
+@@ -87,6 +87,9 @@ static void prepare_ramstage_region(void *resume_backup_memory)
+ #if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK)
+ initialize_romstage_console_lock();
+ #endif
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ initialize_romstage_nvram_cbfs_lock();
++#endif
+
+ print_car_debug("Done\n");
+ }
+diff --git a/src/drivers/pc80/mc146818rtc.c b/src/drivers/pc80/mc146818rtc.c
+index 07fc884..59de0a2 100644
+--- a/src/drivers/pc80/mc146818rtc.c
++++ b/src/drivers/pc80/mc146818rtc.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 The Chromium OS Authors. All rights reserved.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -37,6 +38,11 @@
+ #define LB_CKS_LOC 0
+ #endif
+
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++#include <smp/spinlock.h>
++#endif
++#endif
+
+ static void cmos_reset_date(void)
+ {
+@@ -208,6 +214,12 @@ enum cb_err get_option(void *dest, const char *name)
+ if (!IS_ENABLED(CONFIG_USE_OPTION_TABLE))
+ return CB_CMOS_OTABLE_DISABLED;
+
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_lock(romstage_nvram_cbfs_lock());
++#endif
++#endif
++
+ /* Figure out how long name is */
+ namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
+
+@@ -217,6 +229,11 @@ enum cb_err get_option(void *dest, const char *name)
+ if (!ct) {
+ printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
+ "Options are disabled\n");
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_unlock(romstage_nvram_cbfs_lock());
++#endif
++#endif
+ return CB_CMOS_LAYOUT_NOT_FOUND;
+ }
+ ce = (struct cmos_entries*)((unsigned char *)ct + ct->header_length);
+@@ -229,13 +246,35 @@ enum cb_err get_option(void *dest, const char *name)
+ }
+ if (!found) {
+ printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_unlock(romstage_nvram_cbfs_lock());
++#endif
++#endif
+ return CB_CMOS_OPTION_NOT_FOUND;
+ }
+
+- if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS)
++ if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) {
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_unlock(romstage_nvram_cbfs_lock());
++#endif
++#endif
+ return CB_CMOS_ACCESS_ERROR;
+- if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC))
++ }
++ if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) {
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_unlock(romstage_nvram_cbfs_lock());
++#endif
++#endif
+ return CB_CMOS_CHECKSUM_INVALID;
++ }
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++ spin_unlock(romstage_nvram_cbfs_lock());
++#endif
++#endif
+ return CB_SUCCESS;
+ }
+
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0131-mainboard-asus-kgpe-d16-Enable-CBFS-spinlocks.patch b/resources/libreboot/patch/kgpe-d16/0131-mainboard-asus-kgpe-d16-Enable-CBFS-spinlocks.patch
new file mode 100644
index 00000000..07923987
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0131-mainboard-asus-kgpe-d16-Enable-CBFS-spinlocks.patch
@@ -0,0 +1,66 @@
+From 871017fedbc38dbdaf4fadb1e390046845266b95 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 28 Aug 2015 20:02:45 -0500
+Subject: [PATCH 131/146] mainboard/asus/kgpe-d16: Enable CBFS spinlocks
+
+---
+ src/mainboard/asus/kgpe-d16/Kconfig | 1 +
+ src/mainboard/asus/kgpe-d16/romstage.c | 17 +++++++++++++++--
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index 761fc93..06116a2 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -14,6 +14,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
+ select SUPERIO_NUVOTON_NCT5572D
+ select PARALLEL_CPU_INIT
+ select HAVE_ROMSTAGE_CONSOLE_SPINLOCK
++ select HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK
+ select HAVE_HARD_RESET
+ select HAVE_OPTION_TABLE
+ select HAVE_CMOS_DEFAULT
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index c91fb1d..3740bb0 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -315,6 +315,18 @@ void initialize_romstage_console_lock(void)
+ car_get_var(printk_spinlock) = SPIN_LOCK_UNLOCKED;
+ }
+
++static spinlock_t nvram_cbfs_spinlock CAR_GLOBAL;
++
++spinlock_t* romstage_nvram_cbfs_lock(void)
++{
++ return car_get_var_ptr(&nvram_cbfs_spinlock);
++}
++
++void initialize_romstage_nvram_cbfs_lock(void)
++{
++ car_get_var(nvram_cbfs_spinlock) = SPIN_LOCK_UNLOCKED;
++}
++
+ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
+ uint32_t esp;
+@@ -336,8 +348,9 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ timestamp_init(timestamp_get());
+ timestamp_add_now(TS_START_ROMSTAGE);
+
+- /* Initialize the printk spinlock */
++ /* Initialize the printk and nvram CBFS spinlocks */
+ initialize_romstage_console_lock();
++ initialize_romstage_nvram_cbfs_lock();
+
+ /* Nothing special needs to be done to find bus 0 */
+ /* Allow the HT devices to be found */
+@@ -561,4 +574,4 @@ BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
+ }
+
+ return 0;
+-}
+\ No newline at end of file
++}
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0132-cpu-amd-microcode-Introduce-CBFS-access-spinlock-to-.patch b/resources/libreboot/patch/kgpe-d16/0132-cpu-amd-microcode-Introduce-CBFS-access-spinlock-to-.patch
new file mode 100644
index 00000000..a8005e8a
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0132-cpu-amd-microcode-Introduce-CBFS-access-spinlock-to-.patch
@@ -0,0 +1,151 @@
+From 2b8c4a3914e4d0705f10f09ea570e6f8a9d7ef93 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Fri, 28 Aug 2015 20:48:17 -0500
+Subject: [PATCH 132/146] cpu/amd/microcode: Introduce CBFS access spinlock to
+ avoid IOMMU failure
+
+---
+ src/Kconfig | 4 ++++
+ src/arch/x86/include/arch/smp/spinlock.h | 7 ++++++-
+ src/cpu/amd/microcode/microcode.c | 23 +++++++++++++++++++++++
+ src/mainboard/asus/kgpe-d16/Kconfig | 1 +
+ src/mainboard/asus/kgpe-d16/romstage.c | 15 ++++++++++++++-
+ 5 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/src/Kconfig b/src/Kconfig
+index f1b7ebe..6b0df6a 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -455,6 +455,10 @@ config HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK
+ bool
+ default n
+
++config HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK
++ bool
++ default n
++
+ config HAVE_MONOTONIC_TIMER
+ def_bool n
+ help
+diff --git a/src/arch/x86/include/arch/smp/spinlock.h b/src/arch/x86/include/arch/smp/spinlock.h
+index cf142a9..291c943 100644
+--- a/src/arch/x86/include/arch/smp/spinlock.h
++++ b/src/arch/x86/include/arch/smp/spinlock.h
+@@ -1,7 +1,10 @@
+ #ifndef ARCH_SMP_SPINLOCK_H
+ #define ARCH_SMP_SPINLOCK_H
+
+-#if !defined(__PRE_RAM__) || defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK) || defined(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK)
++#if !defined(__PRE_RAM__) \
++ || defined(CONFIG_HAVE_ROMSTAGE_CONSOLE_SPINLOCK) \
++ || defined(CONFIG_HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK) \
++ || defined(CONFIG_HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK)
+
+ /*
+ * Your basic SMP spinlocks, allowing only a single CPU anywhere
+@@ -16,6 +19,8 @@ spinlock_t* romstage_console_lock(void);
+ void initialize_romstage_console_lock(void);
+ spinlock_t* romstage_nvram_cbfs_lock(void);
+ void initialize_romstage_nvram_cbfs_lock(void);
++spinlock_t* romstage_microcode_cbfs_lock(void);
++void initialize_romstage_microcode_cbfs_lock(void);
+ #endif
+
+ #define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 }
+diff --git a/src/cpu/amd/microcode/microcode.c b/src/cpu/amd/microcode/microcode.c
+index ce5b08f..fc6b0f2 100644
+--- a/src/cpu/amd/microcode/microcode.c
++++ b/src/cpu/amd/microcode/microcode.c
+@@ -24,6 +24,12 @@
+ #include <cpu/amd/microcode.h>
+ #include <cbfs.h>
+
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK)
++#include <smp/spinlock.h>
++#endif
++#endif
++
+ #define UCODE_DEBUG(fmt, args...) \
+ do { printk(BIOS_DEBUG, "[microcode] "fmt, ##args); } while(0)
+
+@@ -106,12 +112,29 @@ void amd_update_microcode_from_cbfs(u32 equivalent_processor_rev_id)
+ return;
+ }
+
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK)
++ spin_lock(romstage_microcode_cbfs_lock());
++#endif
++#endif
++
+ ucode = cbfs_boot_map_with_leak(MICROCODE_CBFS_FILE,
+ CBFS_TYPE_MICROCODE, &ucode_len);
+ if (!ucode) {
+ UCODE_DEBUG("microcode file not found. Skipping updates.\n");
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK)
++ spin_unlock(romstage_microcode_cbfs_lock());
++#endif
++#endif
+ return;
+ }
+
+ amd_update_microcode(ucode, ucode_len, equivalent_processor_rev_id);
++
++#ifdef __PRE_RAM__
++#if IS_ENABLED(CONFIG_HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK)
++ spin_unlock(romstage_microcode_cbfs_lock());
++#endif
++#endif
+ }
+diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig
+index 06116a2..5f421db 100644
+--- a/src/mainboard/asus/kgpe-d16/Kconfig
++++ b/src/mainboard/asus/kgpe-d16/Kconfig
+@@ -15,6 +15,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
+ select PARALLEL_CPU_INIT
+ select HAVE_ROMSTAGE_CONSOLE_SPINLOCK
+ select HAVE_ROMSTAGE_NVRAM_CBFS_SPINLOCK
++ select HAVE_ROMSTAGE_MICROCODE_CBFS_SPINLOCK
+ select HAVE_HARD_RESET
+ select HAVE_OPTION_TABLE
+ select HAVE_CMOS_DEFAULT
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 3740bb0..6b5d801 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -327,6 +327,18 @@ void initialize_romstage_nvram_cbfs_lock(void)
+ car_get_var(nvram_cbfs_spinlock) = SPIN_LOCK_UNLOCKED;
+ }
+
++static spinlock_t microcode_cbfs_spinlock CAR_GLOBAL;
++
++spinlock_t* romstage_microcode_cbfs_lock(void)
++{
++ return car_get_var_ptr(&microcode_cbfs_spinlock);
++}
++
++void initialize_romstage_microcode_cbfs_lock(void)
++{
++ car_get_var(microcode_cbfs_spinlock) = SPIN_LOCK_UNLOCKED;
++}
++
+ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
+ uint32_t esp;
+@@ -348,9 +360,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ timestamp_init(timestamp_get());
+ timestamp_add_now(TS_START_ROMSTAGE);
+
+- /* Initialize the printk and nvram CBFS spinlocks */
++ /* Initialize the printk, nvram CBFS, and microcode CBFS spinlocks */
+ initialize_romstage_console_lock();
+ initialize_romstage_nvram_cbfs_lock();
++ initialize_romstage_microcode_cbfs_lock();
+
+ /* Nothing special needs to be done to find bus 0 */
+ /* Allow the HT devices to be found */
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0133-mainboard-asus-kgpe-d16-Limit-HT-speed-to-2.6GHz.patch b/resources/libreboot/patch/kgpe-d16/0133-mainboard-asus-kgpe-d16-Limit-HT-speed-to-2.6GHz.patch
new file mode 100644
index 00000000..f314aad1
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0133-mainboard-asus-kgpe-d16-Limit-HT-speed-to-2.6GHz.patch
@@ -0,0 +1,38 @@
+From 66fffccceee09c2bffb7ad2de7159cb7e9bbae72 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 3 Sep 2015 17:39:51 -0500
+Subject: [PATCH 133/146] mainboard/asus/kgpe-d16: Limit HT speed to 2.6GHz
+
+The CPU <--> CPU HT wiring on this board has only been validated
+to 2.6GHz. While higher frequencies appear to function initially,
+and in fact function when only one CPU package is installed, dual
+CPU package systems will lock up after around 6 - 12 hours of uptime
+due to presumed HT link errors at the higher (>= 2.8GHz) HT clocks.
+
+If applications are not being used that stress the coherent fabric,
+then the uptime before hang may be much longer. Users attempting
+to overclock the HT links are advised to "burn in test" the HT links
+by running memtester locked to a node with no local memory installed.
+---
+ src/mainboard/asus/kgpe-d16/romstage.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 6b5d801..61b3f09 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -349,6 +349,11 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+
+ struct sys_info *sysinfo = &sysinfo_car;
+
++ /* Limit the maximum HT speed to 2.6GHz to prevent lockups
++ * due to HT CPU <--> CPU wiring not being validated to 3.2GHz
++ */
++ sysinfo->ht_link_cfg.ht_speed_limit = 2600;
++
+ uint32_t bsp_apicid = 0, val;
+ uint8_t byte;
+ msr_t msr;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0134-cpu-amd-model_10xxx-Apply-missing-Family-15h-errata-.patch b/resources/libreboot/patch/kgpe-d16/0134-cpu-amd-model_10xxx-Apply-missing-Family-15h-errata-.patch
new file mode 100644
index 00000000..5f3d3255
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0134-cpu-amd-model_10xxx-Apply-missing-Family-15h-errata-.patch
@@ -0,0 +1,68 @@
+From ac7cef89f29757df10f660a960c16e818d28de03 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 3 Sep 2015 17:43:52 -0500
+Subject: [PATCH 134/146] cpu/amd/model_10xxx: Apply missing Family 15h errata
+ fixes
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 12 ++++++++++++
+ src/northbridge/amd/amdfam10/misc_control.c | 6 ++++++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index af59120..7a84fcb 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -166,6 +166,14 @@ static const struct {
+ 0x0000000C, 0x00000000,
+ 0x0000000C, 0x00000000}, /* Cx and Dx multiple-link processor */
+
++ { OSVW_ID_Length, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000005, 0x00000000,
++ 0x00000005, 0x00000000}, /* OSVW_ID_Length = 0x5 */
++
++ { OSVW_Status, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000010, 0x00000000,
++ 0x00000010, 0x00000000}, /* OsvwId4 = 0x1 */
++
+ { BU_CFG2, AMD_DR_Dx, AMD_PTYPE_ALL,
+ 0x00000000, 1 << (50-32),
+ 0x00000000, 1 << (50-32)}, /* D0 or Above, RdMmExtCfgQwEn*/
+@@ -638,6 +646,10 @@ static const struct {
+ { 3, 0x1b8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00001000, 0x00001000 }, /* [12] = L3PrivReplEn */
+
++ /* Errata 504 workaround */
++ { 3, 0x1b8, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00040000, 0x00040000 }, /* [18] = 1b */
++
+ /* IBS Control Register */
+ { 3, 0x1cc, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00000100, 0x00000100 }, /* [8] = LvtOffsetVal */
+diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
+index 61cf1b6..fcb28c4 100644
+--- a/src/northbridge/amd/amdfam10/misc_control.c
++++ b/src/northbridge/amd/amdfam10/misc_control.c
+@@ -80,6 +80,7 @@ static void mcf3_read_resources(device_t dev)
+
+ static void set_agp_aperture(device_t dev, uint32_t pci_id)
+ {
++ uint32_t dword;
+ struct resource *resource;
+
+ resource = probe_resource(dev, 0x94);
+@@ -110,6 +111,11 @@ static void set_agp_aperture(device_t dev, uint32_t pci_id)
+
+ /* Report the resource has been stored... */
+ report_resource_stored(pdev, resource, " <gart>");
++
++ /* Errata 540 workaround */
++ dword = pci_read_config32(pdev, 0x90);
++ dword |= 0x1 << 6; /* DisGartTblWlkPrb = 0x1 */
++ pci_write_config32(pdev, 0x90, dword);
+ }
+ }
+ }
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0135-northbridge-amd-amdmct-mct_ddr3-Use-StopOnError-to-d.patch b/resources/libreboot/patch/kgpe-d16/0135-northbridge-amd-amdmct-mct_ddr3-Use-StopOnError-to-d.patch
new file mode 100644
index 00000000..bbacc255
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0135-northbridge-amd-amdmct-mct_ddr3-Use-StopOnError-to-d.patch
@@ -0,0 +1,229 @@
+From 6ba4ab07522e4a8dea42f12f8eab6edf648d36eb Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 3 Sep 2015 18:59:53 -0500
+Subject: [PATCH 135/146] northbridge/amd/amdmct/mct_ddr3: Use StopOnError to
+ decrease training time
+
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 84 +++++++++++++++++-------
+ src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 4 +-
+ 2 files changed, 64 insertions(+), 24 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+index 9c9a8c2..77ffa42 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+@@ -1121,7 +1121,7 @@ static void stop_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ }
+
+ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane, uint8_t stop_on_error)
+ {
+ uint32_t dword;
+ uint32_t dev = pDCTstat->dev_dct;
+@@ -1133,24 +1133,35 @@ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ if (lane < 4) {
+ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else if (lane < 8) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
++ } else if (lane == 8) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0x0 */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else if (lane == 0xff) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0xffffffff);
+ Set_NB32_DCT(dev, dct, 0x278, ~0xffffffff);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0x0 */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ }
+
+- dword = Get_NB32_DCT(dev, dct, 0x27c);
+- dword &= ~(0xff); /* EccMask = 0 */
+- if (lane != 0xff)
+- if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+- dword |= 0xff; /* EccMask = 0xff */
+- Set_NB32_DCT(dev, dct, 0x27c, dword);
+-
+ dword = Get_NB32_DCT(dev, dct, 0x270);
+ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */
+ // dword |= (0x55555);
+@@ -1182,7 +1193,8 @@ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x250);
+ dword |= (0x1 << 3); /* ResetAllErr = 1 */
+- dword &= ~(0x1 << 4); /* StopOnErr = 0 */
++ dword &= ~(0x1 << 4); /* StopOnErr = stop_on_error */
++ dword |= (stop_on_error & 0x1) << 4;
+ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */
+ dword |= (0x1 << 8);
+ dword &= ~(0x7 << 5); /* CmdType = 0 (Read) */
+@@ -1202,7 +1214,7 @@ static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ }
+
+ static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+- struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane, uint8_t stop_on_error)
+ {
+ uint32_t dword;
+ uint32_t dev = pDCTstat->dev_dct;
+@@ -1214,24 +1226,35 @@ static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+ if (lane < 4) {
+ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else if (lane < 8) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
++ } else if (lane == 8) {
++ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
++ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0x0 */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else if (lane == 0xff) {
+ Set_NB32_DCT(dev, dct, 0x274, ~0xffffffff);
+ Set_NB32_DCT(dev, dct, 0x278, ~0xffffffff);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword &= ~(0xff); /* EccMask = 0x0 */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ } else {
+ Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+ Set_NB32_DCT(dev, dct, 0x278, ~0x0);
++ dword = Get_NB32_DCT(dev, dct, 0x27c);
++ dword |= 0xff; /* EccMask = 0xff */
++ Set_NB32_DCT(dev, dct, 0x27c, dword);
+ }
+
+- dword = Get_NB32_DCT(dev, dct, 0x27c);
+- dword &= ~(0xff); /* EccMask = 0 */
+- if (lane != 0xff)
+- if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+- dword |= 0xff; /* EccMask = 0xff */
+- Set_NB32_DCT(dev, dct, 0x27c, dword);
+-
+ dword = Get_NB32_DCT(dev, dct, 0x270);
+ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */
+ // dword |= (0x55555);
+@@ -1263,7 +1286,8 @@ static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+
+ dword = Get_NB32_DCT(dev, dct, 0x250);
+ dword |= (0x1 << 3); /* ResetAllErr = 1 */
+- dword &= ~(0x1 << 4); /* StopOnErr = 0 */
++ dword &= ~(0x1 << 4); /* StopOnErr = stop_on_error */
++ dword |= (stop_on_error & 0x1) << 4;
+ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */
+ dword |= (0x1 << 8);
+ dword &= ~(0x7 << 5); /* CmdType = 1 (Write) */
+@@ -1297,6 +1321,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint8_t dual_rank;
+ uint8_t write_iter;
+ uint8_t read_iter;
++ uint8_t check_antiphase;
+ uint16_t initial_write_dqs_delay[MAX_BYTE_LANES];
+ uint16_t initial_read_dqs_delay[MAX_BYTE_LANES];
+ uint16_t initial_write_data_timing[MAX_BYTE_LANES];
+@@ -1378,7 +1403,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ /* 2.10.5.8.4 (2 B)
+ * Write the DRAM training pattern to the test address
+ */
+- write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
++ write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane, 0);
+
+ /* Read current settings of other (previously trained) lanes */
+ read_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
+@@ -1389,6 +1414,12 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ for (current_read_dqs_delay[lane] = 0; current_read_dqs_delay[lane] < 0x40; current_read_dqs_delay[lane] += 2) {
+ print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 current_read_dqs_delay[lane] ", current_read_dqs_delay[lane], 6);
+
++ if ((current_read_dqs_delay[lane] >> 1) >= (32 - 16)) {
++ check_antiphase = 1;
++ } else {
++ check_antiphase = 0;
++ }
++
+ /* 2.10.5.8.4 (2 A i)
+ * Commit the current Read DQS Timing Control settings to the hardware registers
+ */
+@@ -1397,7 +1428,16 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ /* 2.10.5.8.4 (2 A ii)
+ * Read the DRAM training pattern from the test address
+ */
+- read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
++ read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane, ((check_antiphase == 0)?1:0));
++
++ if (check_antiphase == 0) {
++ /* Check for early abort before analyzing per-nibble status */
++ dword = Get_NB32_DCT(dev, dct, 0x264) & 0x1ffffff;
++ if (dword != 0) {
++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][(current_read_dqs_delay[lane] >> 1) + 16] = 0; /* Fail */
++ continue;
++ }
++ }
+
+ /* 2.10.5.8.4 (2 A iii)
+ * Record pass / fail status
+@@ -1407,7 +1447,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][(current_read_dqs_delay[lane] >> 1) + 16] = 0; /* Fail */
+ else
+ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][(current_read_dqs_delay[lane] >> 1) + 16] = 1; /* Pass */
+- if ((current_read_dqs_delay[lane] >> 1) >= (32 - 16)) {
++ if (check_antiphase == 1) {
+ /* Check antiphase results */
+ dword = Get_NB32_DCT(dev, dct, 0x26c) & 0x3ffff;
+ if (dword & (0x3 << (lane * 2)))
+@@ -1630,7 +1670,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
+ uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
+ uint8_t dqs_results_array[1024];
+
+- uint16_t ren_step = 0x40;
++ uint16_t ren_step = 0x40;
+ uint32_t index_reg = 0x98;
+ uint32_t dev = pDCTstat->dev_dct;
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+index b71b327..13adc39 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+@@ -1618,14 +1618,14 @@ static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
+ /* 2.10.5.8.5.1.[2,3]
+ * Write the DRAM training pattern to the test address
+ */
+- write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff);
++ write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff, 0);
+
+ /* 2.10.5.8.5.1.4
+ * Incrementally test each MaxRdLatency candidate
+ */
+ for (; pDCTstat->CH_MaxRdLat[Channel] < 0x3ff; pDCTstat->CH_MaxRdLat[Channel]++) {
+ write_max_read_latency_to_registers(pMCTstat, pDCTstat, Channel, pDCTstat->CH_MaxRdLat[Channel]);
+- read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff);
++ read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, Channel, current_worst_case_total_delay_dimm << 1, 0xff, 0);
+ dword = Get_NB32_DCT(dev, Channel, 0x268) & 0x3ffff;
+ if (!dword)
+ break;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0136-mainboard-asus-kgpe-d16-Enable-GART-by-default.patch b/resources/libreboot/patch/kgpe-d16/0136-mainboard-asus-kgpe-d16-Enable-GART-by-default.patch
new file mode 100644
index 00000000..5f090632
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0136-mainboard-asus-kgpe-d16-Enable-GART-by-default.patch
@@ -0,0 +1,25 @@
+From 9b4656eda209cd6fd8178d15b90016c05855dfa9 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 3 Sep 2015 19:27:40 -0500
+Subject: [PATCH 136/146] mainboard/asus/kgpe-d16: Enable GART by default
+
+---
+ src/mainboard/asus/kgpe-d16/cmos.default | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/mainboard/asus/kgpe-d16/cmos.default b/src/mainboard/asus/kgpe-d16/cmos.default
+index 83c1fe8..bc4c332 100644
+--- a/src/mainboard/asus/kgpe-d16/cmos.default
++++ b/src/mainboard/asus/kgpe-d16/cmos.default
+@@ -23,7 +23,7 @@ maximum_p_state_limit = 0xf
+ probe_filter = Auto
+ l3_cache_partitioning = Disable
+ ieee1394 = Enable
+-gart = Disable
++gart = Enable
+ experimental_memory_speed_boost = Disable
+ power_on_after_fail = On
+ boot_option = Fallback
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0137-northbridge-amd-amdfam10-Fix-incorrect-channel-buffe.patch b/resources/libreboot/patch/kgpe-d16/0137-northbridge-amd-amdfam10-Fix-incorrect-channel-buffe.patch
new file mode 100644
index 00000000..5cdfdbbe
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0137-northbridge-amd-amdfam10-Fix-incorrect-channel-buffe.patch
@@ -0,0 +1,40 @@
+From 73ac68361243375696c486a55a48bed2fdffcafc Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 7 Sep 2015 03:39:15 -0500
+Subject: [PATCH 137/146] northbridge/amd/amdfam10: Fix incorrect channel
+ buffer count configuration
+
+---
+ src/northbridge/amd/amdfam10/northbridge.c | 13 +++++--------
+ 1 file changed, 5 insertions(+), 8 deletions(-)
+
+diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
+index a8d9ce6..fcb26f8 100644
+--- a/src/northbridge/amd/amdfam10/northbridge.c
++++ b/src/northbridge/amd/amdfam10/northbridge.c
+@@ -186,17 +186,14 @@ static void ht_route_link(struct bus *link, scan_state mode)
+ * not correctly configured
+ */
+ busses = pci_read_config32(link->dev, link->cap + 0x14);
+- busses &= 0xff000000;
++ busses &= ~(0xff << 8);
+ busses |= parent->secondary & 0xff;
+- if (mode == HT_ROUTE_CLOSE) {
+- busses |= 0xfeff << 8;
+- } else if (mode == HT_ROUTE_SCAN) {
++ if (mode == HT_ROUTE_CLOSE)
++ busses |= 0xff << 8;
++ else if (mode == HT_ROUTE_SCAN)
+ busses |= ((u32) link->secondary & 0xff) << 8;
+- busses |= 0xfc << 16;
+- } else if (mode == HT_ROUTE_FINAL) {
++ else if (mode == HT_ROUTE_FINAL)
+ busses |= ((u32) link->secondary & 0xff) << 8;
+- busses |= ((u32) link->subordinate & 0xff) << 16;
+- }
+ pci_write_config32(link->dev, link->cap + 0x14, busses);
+
+ if (mode == HT_ROUTE_FINAL) {
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0138-cpu-amd-model_10xxx-Force-iolink-detect-to-either-1-.patch b/resources/libreboot/patch/kgpe-d16/0138-cpu-amd-model_10xxx-Force-iolink-detect-to-either-1-.patch
new file mode 100644
index 00000000..4a97b59a
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0138-cpu-amd-model_10xxx-Force-iolink-detect-to-either-1-.patch
@@ -0,0 +1,35 @@
+From b3f2c1bea85b17840326d6f88793a66b128e367b Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 7 Sep 2015 18:07:03 -0500
+Subject: [PATCH 138/146] cpu/amd/model_10xxx: Force iolink detect to either 1
+ or 0
+
+---
+ src/cpu/amd/model_10xxx/init_cpus.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 0ac893a..f17a439 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1236,7 +1236,7 @@ static void cpuSetAMDPCI(u8 node)
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
+- iolink = (AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++ iolink = !!(AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
+
+ if (!iolink && ganged) {
+ if (probe_filter_enabled) {
+@@ -1388,7 +1388,7 @@ static void cpuSetAMDPCI(u8 node)
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
+- iolink = (AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++ iolink = !!(AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
+
+ /* Set defaults */
+ isoc_rsp_tok_1 = 0;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0139-northbridge-amd-amdht-Fix-XCS-buffer-count-setup-on-.patch b/resources/libreboot/patch/kgpe-d16/0139-northbridge-amd-amdht-Fix-XCS-buffer-count-setup-on-.patch
new file mode 100644
index 00000000..84286798
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0139-northbridge-amd-amdht-Fix-XCS-buffer-count-setup-on-.patch
@@ -0,0 +1,142 @@
+From f668efc985a4ad3c0a2c5b0921f0ef2c4a7f458a Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 7 Sep 2015 18:07:43 -0500
+Subject: [PATCH 139/146] northbridge/amd/amdht: Fix XCS buffer count setup on
+ AMD Family 15h CPUs
+
+---
+ src/northbridge/amd/amdht/h3ncmn.c | 94 +++++++++++++++++++++++++++++++++++-
+ 1 file changed, 92 insertions(+), 2 deletions(-)
+
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index 841fc0c..80fe7ce 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -2,6 +2,7 @@
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 Advanced Micro Devices, Inc.
++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -1393,6 +1394,75 @@ static uint32_t fam10NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
+
+ /***************************************************************************//**
+ *
++ * static u16
++ * fam15NorthBridgeFreqMask(u8 NodeID, cNorthBridge *nb)
++ *
++ * Description:
++ * Return a mask that eliminates HT frequencies that cannot be used due to a slow
++ * northbridge frequency.
++ *
++ * Parameters:
++ * @param[in] node = Result could (later) be for a specific node
++ * @param[in] *nb = this northbridge
++ * @return = Frequency mask
++ *
++ ******************************************************************************/
++static uint32_t fam15NorthBridgeFreqMask(u8 node, cNorthBridge *nb)
++{
++ u8 nbCOF;
++ uint32_t supported;
++
++ nbCOF = getMinNbCOF();
++ /*
++ * nbCOF is minimum northbridge speed in hundreds of MHz.
++ * HT can not go faster than the minimum speed of the northbridge.
++ */
++ if ((nbCOF >= 6) && (nbCOF < 10))
++ {
++ /* Generation 1 HT link frequency */
++ /* Convert frequency to bit and all less significant bits,
++ * by setting next power of 2 and subtracting 1.
++ */
++ supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
++ }
++ else if ((nbCOF >= 10) && (nbCOF <= 32))
++ {
++ /* Generation 3 HT link frequency
++ * Assume error retry is enabled on all Gen 3 links
++ */
++ nbCOF *= 2;
++ if (nbCOF > 32)
++ nbCOF = 32;
++
++ /* Convert frequency to bit and all less significant bits,
++ * by setting next power of 2 and subtracting 1.
++ */
++ supported = ((uint32_t)1 << ((nbCOF >> 1) + 2)) - 1;
++ }
++ else if (nbCOF > 32)
++ {
++ supported = HT_FREQUENCY_LIMIT_3200M;
++ }
++ /* unlikely cases, but include as a defensive measure, also avoid trick above */
++ else if (nbCOF == 4)
++ {
++ supported = HT_FREQUENCY_LIMIT_400M;
++ }
++ else if (nbCOF == 2)
++ {
++ supported = HT_FREQUENCY_LIMIT_200M;
++ }
++ else
++ {
++ STOP_HERE;
++ supported = HT_FREQUENCY_LIMIT_200M;
++ }
++
++ return (fixEarlySampleFreqCapability(supported));
++}
++
++/***************************************************************************//**
++ *
+ * static void
+ * gatherLinkData(sMainData *pDat, cNorthBridge *nb)
+ *
+@@ -2266,6 +2336,26 @@ static void fam10BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
+ }
+ }
+
++/***************************************************************************//**
++ *
++ * static void
++ * fam15BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
++ *
++ * Description:
++ * Buffer tunings are inherently northbridge specific. Check for specific configs
++ * which require adjustments and apply any standard workarounds to this node.
++ *
++ * Parameters:
++ * @param[in] node = the node to tune
++ * @param[in] *pDat = global state
++ * @param[in] nb = this northbridge
++ *
++ ******************************************************************************/
++static void fam15BufferOptimizations(u8 node, sMainData *pDat, cNorthBridge *nb)
++{
++ /* Buffer count setup on Family 15h is currently handled in cpuSetAMDPCI */
++}
++
+ /*
+ * North Bridge 'constructor'.
+ *
+@@ -2324,11 +2414,11 @@ void newNorthBridge(u8 node, cNorthBridge *nb)
+ ht3SetCFGAddrMap,
+ convertBitsToWidth,
+ convertWidthToBits,
+- fam10NorthBridgeFreqMask,
++ fam15NorthBridgeFreqMask,
+ gatherLinkData,
+ setLinkData,
+ ht3WriteTrafficDistribution,
+- fam10BufferOptimizations,
++ fam15BufferOptimizations,
+ 0x00000001,
+ 0x00000200,
+ 18,
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0140-cpu-amd-model_10xxx-Fix-link-type-detection-and-XCS-.patch b/resources/libreboot/patch/kgpe-d16/0140-cpu-amd-model_10xxx-Fix-link-type-detection-and-XCS-.patch
new file mode 100644
index 00000000..17c6149e
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0140-cpu-amd-model_10xxx-Fix-link-type-detection-and-XCS-.patch
@@ -0,0 +1,156 @@
+From 9f347bbef949f4a9f402e0ecfac91f7c58bafd84 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Mon, 7 Sep 2015 22:26:55 -0500
+Subject: [PATCH 140/146] cpu/amd/model_10xxx: Fix link type detection and XCS
+ buffer count setup
+
+---
+ src/cpu/amd/model_10xxx/fidvid.c | 2 +-
+ src/cpu/amd/model_10xxx/init_cpus.c | 33 +++++++++++++++++++--------------
+ 2 files changed, 20 insertions(+), 15 deletions(-)
+
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 1d55275..110a299 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -379,7 +379,7 @@ static u32 nb_clk_did(int node, uint64_t cpuRev, uint8_t procPkg) {
+ u8 link0isGen3 = 0;
+ u8 offset;
+ if (AMD_CpuFindCapability(node, 0, &offset)) {
+- link0isGen3 = (AMD_checkLinkType(node, 0, offset) & HTPHY_LINKTYPE_HT3 );
++ link0isGen3 = (AMD_checkLinkType(node, offset) & HTPHY_LINKTYPE_HT3 );
+ }
+ /* FIXME: NB_CLKDID should be 101b for AMD_DA_C2 in package
+ S1g3 in link Gen3 mode, but I don't know how to tell
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index f17a439..0604ef8 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -845,7 +845,7 @@ static BOOL AMD_CpuFindCapability(u8 node, u8 cap_count, u8 * offset)
+ *
+ * Returns the link characteristic mask.
+ */
+-static u32 AMD_checkLinkType(u8 node, u8 link, u8 regoff)
++static u32 AMD_checkLinkType(u8 node, u8 regoff)
+ {
+ uint32_t val;
+ uint32_t val2;
+@@ -876,7 +876,7 @@ static u32 AMD_checkLinkType(u8 node, u8 link, u8 regoff)
+ linktype |= HTPHY_LINKTYPE_HT1;
+
+ /* Check ganged */
+- val = pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170);
++ val = pci_read_config32(NODE_PCI(node, 0), (((regoff - 0x80) / 0x20) << 2) + 0x170);
+
+ if (val & 1)
+ linktype |= HTPHY_LINKTYPE_GANGED;
+@@ -1116,7 +1116,7 @@ static void cpuSetAMDPCI(u8 node)
+ */
+ for (j = 0; j < 4; j++) {
+ if (AMD_CpuFindCapability(node, j, &offset)) {
+- if (AMD_checkLinkType(node, j, offset)
++ if (AMD_checkLinkType(node, offset)
+ & fam10_htphy_default[i].linktype) {
+ AMD_SetHtPhyRegister(node, j,
+ i);
+@@ -1214,6 +1214,7 @@ static void cpuSetAMDPCI(u8 node)
+ pci_write_config32(NODE_PCI(node, 3), 0x1a0, dword);
+
+ uint8_t link;
++ uint8_t link_real;
+ uint8_t ganged;
+ uint8_t iolink;
+ uint8_t probe_filter_enabled = !!dual_node;
+@@ -1235,8 +1236,9 @@ static void cpuSetAMDPCI(u8 node)
+
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+- ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
+- iolink = !!(AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++ link_real = (offset - 0x80) / 0x20;
++ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link_real << 2) + 0x170) & 0x1);
++ iolink = !!(AMD_checkLinkType(node, offset) & HTPHY_LINKTYPE_NONCOHERENT);
+
+ if (!iolink && ganged) {
+ if (probe_filter_enabled) {
+@@ -1332,7 +1334,7 @@ static void cpuSetAMDPCI(u8 node)
+ np_req_cmd = 12;
+ }
+
+- dword = pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x94);
++ dword = pci_read_config32(NODE_PCI(node, 0), (link_real * 0x20) + 0x94);
+ dword &= ~(0x3 << 27); /* IsocRspData = isoc_rsp_data */
+ dword |= ((isoc_rsp_data & 0x3) << 27);
+ dword &= ~(0x3 << 25); /* IsocNpReqData = isoc_np_req_data */
+@@ -1343,9 +1345,9 @@ static void cpuSetAMDPCI(u8 node)
+ dword |= ((isoc_preq & 0x7) << 19);
+ dword &= ~(0x7 << 16); /* IsocNpReqCmd = isoc_np_req_cmd */
+ dword |= ((isoc_np_req_cmd & 0x7) << 16);
+- pci_write_config32(NODE_PCI(node, 0), (link * 0x20) + 0x94, dword);
++ pci_write_config32(NODE_PCI(node, 0), (link_real * 0x20) + 0x94, dword);
+
+- dword = pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x90);
++ dword = pci_read_config32(NODE_PCI(node, 0), (link_real * 0x20) + 0x90);
+ dword &= ~(0x1 << 31); /* LockBc = 0x1 */
+ dword |= ((0x1 & 0x1) << 31);
+ dword &= ~(0x7 << 25); /* FreeData = free_data */
+@@ -1364,7 +1366,7 @@ static void cpuSetAMDPCI(u8 node)
+ dword |= ((preq & 0x7) << 5);
+ dword &= ~(0x1f << 0); /* NpReqCmd = np_req_cmd */
+ dword |= ((np_req_cmd & 0x1f) << 0);
+- pci_write_config32(NODE_PCI(node, 0), (link * 0x20) + 0x90, dword);
++ pci_write_config32(NODE_PCI(node, 0), (link_real * 0x20) + 0x90, dword);
+ }
+ }
+
+@@ -1387,8 +1389,9 @@ static void cpuSetAMDPCI(u8 node)
+
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+- ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link << 2) + 0x170) & 0x1);
+- iolink = !!(AMD_checkLinkType(node, link, offset) & HTPHY_LINKTYPE_NONCOHERENT);
++ link_real = (offset - 0x80) / 0x20;
++ ganged = !!(pci_read_config32(NODE_PCI(node, 0), (link_real << 2) + 0x170) & 0x1);
++ iolink = !!(AMD_checkLinkType(node, offset) & HTPHY_LINKTYPE_NONCOHERENT);
+
+ /* Set defaults */
+ isoc_rsp_tok_1 = 0;
+@@ -1616,7 +1619,7 @@ static void cpuSetAMDPCI(u8 node)
+ }
+ }
+
+- dword = pci_read_config32(NODE_PCI(node, 3), (link << 2) + 0x148);
++ dword = pci_read_config32(NODE_PCI(node, 3), (link_real << 2) + 0x148);
+ dword &= ~(0x3 << 30); /* FreeTok[3:2] = free_tokens[3:2] */
+ dword |= (((free_tokens >> 2) & 0x3) << 30);
+ dword &= ~(0x1 << 28); /* IsocRspTok1 = isoc_rsp_tok_1 */
+@@ -1649,7 +1652,7 @@ static void cpuSetAMDPCI(u8 node)
+ dword |= (((preq_tok_0) & 0x3) << 2);
+ dword &= ~(0x3 << 0); /* ReqTok0 = req_tok_0 */
+ dword |= (((req_tok_0) & 0x3) << 0);
+- pci_write_config32(NODE_PCI(node, 3), (link << 2) + 0x148, dword);
++ pci_write_config32(NODE_PCI(node, 3), (link_real << 2) + 0x148, dword);
+ }
+ }
+
+@@ -1695,6 +1698,7 @@ static void cpuSetAMDPCI(u8 node)
+ }
+
+ uint8_t link;
++ uint8_t link_real;
+ uint8_t isochronous;
+ uint8_t isochronous_link_present;
+
+@@ -1702,7 +1706,8 @@ static void cpuSetAMDPCI(u8 node)
+ isochronous_link_present = 0;
+ for (link = 0; link < 4; link++) {
+ if (AMD_CpuFindCapability(node, link, &offset)) {
+- isochronous = (pci_read_config32(NODE_PCI(node, 0), (link * 0x20) + 0x84) >> 12) & 0x1;
++ link_real = (offset - 0x80) / 0x20;
++ isochronous = (pci_read_config32(NODE_PCI(node, 0), (link_real * 0x20) + 0x84) >> 12) & 0x1;
+
+ if (isochronous)
+ isochronous_link_present = 1;
+--
+1.7.9.5
+
diff --git a/resources/libreboot/patch/kgpe-d16/0141-cpu-amd-model_10xxx-Enable-DFE-on-Family-15h-HT3-lin.patch b/resources/libreboot/patch/kgpe-d16/0141-cpu-amd-model_10xxx-Enable-DFE-on-Family-15h-HT3-lin.patch
new file mode 100644
index 00000000..3968d101
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0141-cpu-amd-model_10xxx-Enable-DFE-on-Family-15h-HT3-lin.patch
@@ -0,0 +1,44 @@
+From c8c36ead5561c661604bd2e141be91091a6e8a99 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Sun, 13 Sep 2015 15:54:32 -0500
+Subject: [PATCH 141/146] cpu/amd/model_10xxx: Enable DFE on Family 15h HT3
+ links
+
+---
+ src/cpu/amd/model_10xxx/defaults.h | 8 ++++++++
+ src/cpu/amd/model_10xxx/init_cpus.c | 2 +-
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 7a84fcb..b906866 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -857,4 +857,12 @@ static const struct {
+ { 0xC0, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+ [20:16] RttIndex = 04h */
++
++ { 0xc4, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x00013480, 0x0003fc80 }, /* [17:10] DCV = 0x4d,
++ [7] DfeEn = 0x1 */
++
++ { 0xd4, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ 0x00013480, 0x0003fc80 }, /* [17:10] DCV = 0x4d,
++ [7] DfeEn = 0x1 */
+ };
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 0604ef8..2c106fe 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -1186,7 +1186,7 @@ static void cpuSetAMDPCI(u8 node)
+ /* Check for dual node capability */
+ if (f3xe8 & 0x20000000)
+ dual_node = 1;
+-
++
+ /* Determine the number of active compute units on this node */
+ f5x80 = pci_read_config32(NODE_PCI(node, 5), 0x80);
+ cu_enabled = f5x80 & 0xf;
+--
+1.7.9.5
+