coreboot-gerrit
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
February 2017
- 1 participants
- 1019 discussions

Patch set updated for coreboot: soc/intel/skylake: add SGX initialization
by Robbie Zhang Feb. 28, 2017
by Robbie Zhang Feb. 28, 2017
Feb. 28, 2017
Robbie Zhang (robbie.zhang(a)intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18445
-gerrit
commit 3cb41feeb32a5ab946bf9e6aac6656bddef7708b
Author: Robbie Zhang <robbie.zhang(a)intel.com>
Date: Tue Feb 21 14:00:31 2017 -0800
soc/intel/skylake: add SGX initialization
This patch implements SGX initialization steps in corboot per Intel SGX
BWG for Kabylake SoC. If enabled on a Kabylake device, SoC capability and
PRM (processor reserved memory) of desired size (needs to be configured
through PrmrrSize) are provisioned for later software stack to use SGX
(i.e., run SGX enclaves).
One issue is still puzzling and needs to be addressed: by calling
configure_sgx() in cpu_core_init() which is the per-thread function, SGX
is always failing for thread 0 but is successful for other 3 threads.
I had to call configure_sgx() again from soc_init_cpus() which is the
BSP-only function to make it enable on the BSP.
Another pending work is the implementation for the Owner Epoch update
which shall be added later.
BUG=chrome-os-partner:62438
BRANCH=NONE
TEST=Tested on Eve, verified SGX activation is successful on all threads.
Change-Id: I8b64284875eae061fa8e7a01204d48d320a285a9
Signed-off-by: Robbie Zhang <robbie.zhang(a)intel.com>
---
src/soc/intel/skylake/Makefile.inc | 1 +
src/soc/intel/skylake/chip.h | 3 +
src/soc/intel/skylake/cpu.c | 51 +++++++---
src/soc/intel/skylake/include/soc/cpu.h | 1 +
src/soc/intel/skylake/include/soc/msr.h | 11 ++-
src/soc/intel/skylake/sgx.c | 161 ++++++++++++++++++++++++++++++++
6 files changed, 213 insertions(+), 15 deletions(-)
diff --git a/src/soc/intel/skylake/Makefile.inc b/src/soc/intel/skylake/Makefile.inc
index 4b6fcfc..25f65d0 100644
--- a/src/soc/intel/skylake/Makefile.inc
+++ b/src/soc/intel/skylake/Makefile.inc
@@ -80,6 +80,7 @@ ramstage-y += pmutil.c
ramstage-$(CONFIG_PLATFORM_USES_FSP2_0) += reset.c
ramstage-y += sata.c
ramstage-y += sd.c
+ramstage-y += sgx.c
ramstage-y += smbus.c
ramstage-y += smbus_common.c
ramstage-y += smi.c
diff --git a/src/soc/intel/skylake/chip.h b/src/soc/intel/skylake/chip.h
index 2e4adb2..4255069 100644
--- a/src/soc/intel/skylake/chip.h
+++ b/src/soc/intel/skylake/chip.h
@@ -410,6 +410,9 @@ struct soc_intel_skylake_config {
/* Wake Enable Bitmap for USB3 ports */
u8 usb3_wake_enable_bitmap;
+
+ /* Enable SGX feature */
+ u8 sgx_enable;
};
typedef struct soc_intel_skylake_config config_t;
diff --git a/src/soc/intel/skylake/cpu.c b/src/soc/intel/skylake/cpu.c
index d1a684c..0cbed78 100644
--- a/src/soc/intel/skylake/cpu.c
+++ b/src/soc/intel/skylake/cpu.c
@@ -42,6 +42,10 @@
#include <soc/smm.h>
#include <soc/systemagent.h>
+/* MP initialization support. */
+static const void *microcode_patch;
+static int ht_disabled;
+
/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */
static const u8 power_limit_time_sec_to_msr[] = {
[0] = 0x00,
@@ -336,11 +340,10 @@ static void set_energy_perf_bias(u8 policy)
static void configure_mca(void)
{
msr_t msr;
- const unsigned int mcg_cap_msr = 0x179;
int i;
int num_banks;
- msr = rdmsr(mcg_cap_msr);
+ msr = rdmsr(IA32_MCG_CAP);
num_banks = msr.lo & 0xff;
msr.lo = msr.hi = 0;
/*
@@ -348,8 +351,13 @@ static void configure_mca(void)
* of these banks are core vs package scope. For now every CPU clears
* every bank.
*/
- for (i = 0; i < num_banks; i++)
+ for (i = 0; i < num_banks; i++) {
+ /* Clear the machine check status */
wrmsr(IA32_MC0_STATUS + (i * 4), msr);
+ /* Initialize machine checks */
+ wrmsr(IA32_MC0_CTL + i * 4,
+ (msr_t) {.lo = 0xffffffff, .hi = 0xffffffff});
+ }
}
/* All CPUs including BSP will run the following function. */
@@ -376,6 +384,9 @@ static void cpu_core_init(device_t cpu)
/* Enable Turbo */
enable_turbo();
+
+ /* Configure SGX */
+ configure_sgx(microcode_patch);
}
static struct device_operations cpu_dev_ops = {
@@ -399,10 +410,6 @@ static const struct cpu_driver driver __cpu_driver = {
.id_table = cpu_table,
};
-/* MP initialization support. */
-static const void *microcode_patch;
-static int ht_disabled;
-
static int get_cpu_count(void)
{
msr_t msr;
@@ -501,6 +508,14 @@ static void soc_init_cpus(void *unused)
/* Thermal throttle activation offset */
configure_thermal_target();
+
+ /*
+ * TODO: somehow calling configure_sgx() in cpu_core_init() is not
+ * successful on the BSP (other threads are fine). Have to run it again
+ * here to get SGX enabled on BSP. This behavior needs to root-caused
+ * and we shall not have this redundant call.
+ */
+ configure_sgx(microcode_patch);
}
/* Ensure to re-program all MTRRs based on DRAM resource settings */
@@ -512,15 +527,25 @@ static void soc_post_cpus_init(void *unused)
int soc_skip_ucode_update(u32 current_patch_id, u32 new_patch_id)
{
- msr_t msr;
- /* If PRMRR/SGX is supported the FIT microcode load will set the msr
+ msr_t msr1;
+ msr_t msr2;
+
+ /*
+ * If PRMRR/SGX is supported the FIT microcode load will set the msr
* 0x08b with the Patch revision id one less than the id in the
* microcode binary. The PRMRR support is indicated in the MSR
- * MTRRCAP[12]. Check for this feature and avoid reloading the
- * same microcode during CPU initialization.
+ * MTRRCAP[12]. If SGX is not enabled, check and avoid reloading the
+ * same microcode during CPU initialization. If SGX is enabled, as
+ * part of SGX BIOS initialization steps, the same microcode needs to
+ * be reloaded after the core PRMRR MSRs are programmed.
*/
- msr = rdmsr(MTRR_CAP_MSR);
- return (msr.lo & PRMRR_SUPPORTED) && (current_patch_id == new_patch_id - 1);
+ msr1 = rdmsr(MTRR_CAP_MSR);
+ msr2 = rdmsr(PRMRR_PHYS_BASE_MSR);
+ if (msr2.lo && (current_patch_id == new_patch_id - 1))
+ return 0;
+ else
+ return (msr1.lo & PRMRR_SUPPORTED) &&
+ (current_patch_id == new_patch_id - 1);
}
/*
diff --git a/src/soc/intel/skylake/include/soc/cpu.h b/src/soc/intel/skylake/include/soc/cpu.h
index 33fb2f1..a259a2b 100644
--- a/src/soc/intel/skylake/include/soc/cpu.h
+++ b/src/soc/intel/skylake/include/soc/cpu.h
@@ -68,5 +68,6 @@ u32 cpu_family_model(void);
u32 cpu_stepping(void);
int cpu_is_ult(void);
int is_secondary_thread(void);
+void configure_sgx(const void *microcode_patch);
#endif
diff --git a/src/soc/intel/skylake/include/soc/msr.h b/src/soc/intel/skylake/include/soc/msr.h
index 4d295e1..4f05ba6 100644
--- a/src/soc/intel/skylake/include/soc/msr.h
+++ b/src/soc/intel/skylake/include/soc/msr.h
@@ -22,11 +22,13 @@
#define IA32_FEATURE_CONTROL 0x3a
#define CPUID_VMX (1 << 5)
#define CPUID_SMX (1 << 6)
+#define MSR_BIOS_UPGD_TRIG 0x7a
#define MSR_PLATFORM_INFO 0xce
#define PLATFORM_INFO_SET_TDP (1 << 29)
#define MSR_PMG_CST_CONFIG_CONTROL 0xe2
#define MSR_PMG_IO_CAPTURE_BASE 0xe4
#define MSR_FEATURE_CONFIG 0x13c
+#define IA32_MCG_CAP 0x179
#define SMM_MCA_CAP_MSR 0x17d
#define SMM_CPU_SVRSTR_BIT 57
#define SMM_CPU_SVRSTR_MASK (1 << (SMM_CPU_SVRSTR_BIT - 32))
@@ -48,13 +50,18 @@
#define ENERGY_POLICY_NORMAL 6
#define ENERGY_POLICY_POWERSAVE 15
#define IA32_PACKAGE_THERM_INTERRUPT 0x1b2
-#define EMRR_PHYS_BASE_MSR 0x1f4
-#define EMRR_PHYS_MASK_MSR 0x1f5
+#define PRMRR_PHYS_BASE_MSR 0x1f4
+#define PRMRR_PHYS_MASK_MSR 0x1f5
+#define PRMRR_PHYS_MASK_LOCK (1 << 10)
+#define PRMRR_PHYS_MASK_VALID (1 << 11)
#define IA32_PLATFORM_DCA_CAP 0x1f8
#define MSR_POWER_CTL 0x1fc
#define MSR_LT_LOCK_MEMORY 0x2e7
#define UNCORE_PRMRR_PHYS_BASE_MSR 0x2f4
#define UNCORE_PRMRR_PHYS_MASK_MSR 0x2f5
+#define MSR_SGX_OWNEREPOCH0 0x300
+#define MSR_SGX_OWNEREPOCH1 0x301
+#define IA32_MC0_CTL 0x400
#define IA32_MC0_STATUS 0x401
#define SMM_FEATURE_CONTROL_MSR 0x4e0
#define SMM_CPU_SAVE_EN (1 << 1)
diff --git a/src/soc/intel/skylake/sgx.c b/src/soc/intel/skylake/sgx.c
new file mode 100644
index 0000000..4a2386f
--- /dev/null
+++ b/src/soc/intel/skylake/sgx.c
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <console/console.h>
+#include <chip.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/intel/microcode.h>
+#include <soc/cpu.h>
+#include <soc/msr.h>
+#include <soc/pci_devs.h>
+
+static int is_sgx_supported(void)
+{
+ struct cpuid_result cpuid_regs;
+ msr_t msr;
+
+ cpuid_regs = cpuid_ext(0x7, 0x0); /* EBX[2] is feature capability */
+ msr = rdmsr(MTRR_CAP_MSR); /* Bit 12 is PRMRR enablement */
+ return ((cpuid_regs.ebx & 0x4) && (msr.lo & PRMRR_SUPPORTED));
+}
+
+static int configure_core_prmrr(void)
+{
+ msr_t prmrr_base;
+ msr_t prmrr_mask;
+ msr_t msr;
+
+ /*
+ * PRMRR base and mask are read from the UNCORE PRMRR MSRs
+ * that are already set in FSP-M.
+ */
+ prmrr_base = rdmsr(UNCORE_PRMRR_PHYS_BASE_MSR);
+ prmrr_mask = rdmsr(UNCORE_PRMRR_PHYS_MASK_MSR);
+ if (!prmrr_base.lo) {
+ printk(BIOS_ERR, "SGX Error: Uncore PRMRR is not set!\n");
+ return -1;
+ }
+
+ msr = rdmsr(PRMRR_PHYS_MASK_MSR);
+ /* If it is locked don't attempt to write PRMRR MSRs. */
+ if (msr.lo & PRMRR_PHYS_MASK_LOCK)
+ return 0;
+
+ /* Program core PRMRR MSRs */
+ prmrr_base.lo |= 0x6; /* Set memory attribute to cache writeback */
+ wrmsr(PRMRR_PHYS_BASE_MSR, prmrr_base);
+ prmrr_mask.lo &= ~PRMRR_PHYS_MASK_VALID; /* Do not set the valid bit */
+ prmrr_mask.lo |= PRMRR_PHYS_MASK_LOCK; /* Lock it */
+ wrmsr(PRMRR_PHYS_MASK_MSR, prmrr_mask);
+ return 0;
+}
+
+static void enable_sgx(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(IA32_FEATURE_CONTROL);
+ /* Only enable it when it is not locked */
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= (1 << 18); /* Enable it */
+ wrmsr(IA32_FEATURE_CONTROL, msr);
+ }
+}
+
+static void lock_sgx(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(IA32_FEATURE_CONTROL);
+ /* If it is locked don't attempt to lock it again. */
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= 1; /* Lock it */
+ wrmsr(IA32_FEATURE_CONTROL, msr);
+ }
+}
+
+static int owner_epoch_update(void)
+{
+ /*
+ * TODO - the Owner Epoch update mechanism is not determined yet,
+ * for PoC just write '0's to the MSRs.
+ */
+ msr_t msr = {0, 0};
+
+ wrmsr(MSR_SGX_OWNEREPOCH0, msr);
+ wrmsr(MSR_SGX_OWNEREPOCH1, msr);
+ return 0;
+}
+
+static void activate_sgx(void)
+{
+ msr_t msr;
+
+ /*
+ * Activate SGX feature by writing 1b to MSR 0x7A on all threads.
+ * BIOS must ensure bit 0 is set prior to writing to it, then read it
+ * back and verify the bit is cleared to confirm SGX activation.
+ */
+ msr = rdmsr(MSR_BIOS_UPGD_TRIG);
+ if (msr.lo & 0x1) {
+ wrmsr(MSR_BIOS_UPGD_TRIG, (msr_t) {.lo = 0x1, .hi = 0});
+ /* Read back to verify it is activated */
+ msr = rdmsr(MSR_BIOS_UPGD_TRIG);
+ if (msr.lo & 0x1)
+ printk(BIOS_DEBUG, "SGX activation failed.\n");
+ else
+ printk(BIOS_DEBUG, "SGX activation was successful.\n");
+ } else {
+ printk(BIOS_DEBUG, "SGX feature is deactivated.\n");
+ }
+}
+
+void configure_sgx(const void *microcode_patch)
+{
+ device_t dev = SA_DEV_ROOT;
+ config_t *conf = dev->chip_info;
+ msr_t msr;
+
+ if (!conf->sgx_enable || !is_sgx_supported())
+ return;
+
+ /* Initialize PRMRR core MSRs */
+ if (configure_core_prmrr() < 0)
+ return;
+
+ /* Enable the SGX feature */
+ enable_sgx();
+
+ /* Update the owner epoch value */
+ if (owner_epoch_update() < 0)
+ return;
+
+ /* Ensure to lock memory before reload microcode patch */
+ msr = rdmsr(MSR_LT_LOCK_MEMORY);
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= 1; /* Lock it */
+ wrmsr(MSR_LT_LOCK_MEMORY, msr);
+ }
+
+ /* Reload the microcode patch */
+ intel_microcode_load_unlocked(microcode_patch);
+
+ /* Lock the SGX feature */
+ lock_sgx();
+
+ /* Activate the SGX feature */
+ activate_sgx();
+}
1
0

Patch set updated for coreboot: soc/intel/skylake: add SGX initialization
by Robbie Zhang Feb. 28, 2017
by Robbie Zhang Feb. 28, 2017
Feb. 28, 2017
Robbie Zhang (robbie.zhang(a)intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18445
-gerrit
commit 19d69b2c24c7b5187abb4a816d228121d15ecc54
Author: Robbie Zhang <robbie.zhang(a)intel.com>
Date: Tue Feb 21 14:00:31 2017 -0800
soc/intel/skylake: add SGX initialization
This patch implements SGX initialization steps in corboot per Intel SGX
BWG for Kabylake SoC. If enabled on a Kabylake device, SoC capability and
PRM (processor reserved memory) of desired size (needs to be configured
through PrmrrSize) are provisioned for later software stack to use SGX
(i.e., run SGX enclaves).
One issue is still puzzling and needs to be addressed: by calling
configure_sgx() in cpu_core_init() which is the per-thread function, SGX
is always failing for thread 0 but is successful for other 3 threads.
I had to call configure_sgx() again from soc_init_cpus() which is the
BSP-only function to make it enable on the BSP.
Another pending work is the implementation for the Owner Epoch update
which shall be added later.
BUG=chrome-os-partner:62438
BRANCH=NONE
TEST=Tested on Eve, verified SGX activation is successful on all threads.
Change-Id: I8b64284875eae061fa8e7a01204d48d320a285a9
Signed-off-by: Robbie Zhang <robbie.zhang(a)intel.com>
---
src/soc/intel/skylake/Makefile.inc | 1 +
src/soc/intel/skylake/chip.h | 3 +
src/soc/intel/skylake/cpu.c | 51 +++++++---
src/soc/intel/skylake/include/soc/cpu.h | 1 +
src/soc/intel/skylake/include/soc/msr.h | 11 ++-
src/soc/intel/skylake/sgx.c | 162 ++++++++++++++++++++++++++++++++
6 files changed, 214 insertions(+), 15 deletions(-)
diff --git a/src/soc/intel/skylake/Makefile.inc b/src/soc/intel/skylake/Makefile.inc
index 4b6fcfc..25f65d0 100644
--- a/src/soc/intel/skylake/Makefile.inc
+++ b/src/soc/intel/skylake/Makefile.inc
@@ -80,6 +80,7 @@ ramstage-y += pmutil.c
ramstage-$(CONFIG_PLATFORM_USES_FSP2_0) += reset.c
ramstage-y += sata.c
ramstage-y += sd.c
+ramstage-y += sgx.c
ramstage-y += smbus.c
ramstage-y += smbus_common.c
ramstage-y += smi.c
diff --git a/src/soc/intel/skylake/chip.h b/src/soc/intel/skylake/chip.h
index 2e4adb2..4255069 100644
--- a/src/soc/intel/skylake/chip.h
+++ b/src/soc/intel/skylake/chip.h
@@ -410,6 +410,9 @@ struct soc_intel_skylake_config {
/* Wake Enable Bitmap for USB3 ports */
u8 usb3_wake_enable_bitmap;
+
+ /* Enable SGX feature */
+ u8 sgx_enable;
};
typedef struct soc_intel_skylake_config config_t;
diff --git a/src/soc/intel/skylake/cpu.c b/src/soc/intel/skylake/cpu.c
index d1a684c..0cbed78 100644
--- a/src/soc/intel/skylake/cpu.c
+++ b/src/soc/intel/skylake/cpu.c
@@ -42,6 +42,10 @@
#include <soc/smm.h>
#include <soc/systemagent.h>
+/* MP initialization support. */
+static const void *microcode_patch;
+static int ht_disabled;
+
/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */
static const u8 power_limit_time_sec_to_msr[] = {
[0] = 0x00,
@@ -336,11 +340,10 @@ static void set_energy_perf_bias(u8 policy)
static void configure_mca(void)
{
msr_t msr;
- const unsigned int mcg_cap_msr = 0x179;
int i;
int num_banks;
- msr = rdmsr(mcg_cap_msr);
+ msr = rdmsr(IA32_MCG_CAP);
num_banks = msr.lo & 0xff;
msr.lo = msr.hi = 0;
/*
@@ -348,8 +351,13 @@ static void configure_mca(void)
* of these banks are core vs package scope. For now every CPU clears
* every bank.
*/
- for (i = 0; i < num_banks; i++)
+ for (i = 0; i < num_banks; i++) {
+ /* Clear the machine check status */
wrmsr(IA32_MC0_STATUS + (i * 4), msr);
+ /* Initialize machine checks */
+ wrmsr(IA32_MC0_CTL + i * 4,
+ (msr_t) {.lo = 0xffffffff, .hi = 0xffffffff});
+ }
}
/* All CPUs including BSP will run the following function. */
@@ -376,6 +384,9 @@ static void cpu_core_init(device_t cpu)
/* Enable Turbo */
enable_turbo();
+
+ /* Configure SGX */
+ configure_sgx(microcode_patch);
}
static struct device_operations cpu_dev_ops = {
@@ -399,10 +410,6 @@ static const struct cpu_driver driver __cpu_driver = {
.id_table = cpu_table,
};
-/* MP initialization support. */
-static const void *microcode_patch;
-static int ht_disabled;
-
static int get_cpu_count(void)
{
msr_t msr;
@@ -501,6 +508,14 @@ static void soc_init_cpus(void *unused)
/* Thermal throttle activation offset */
configure_thermal_target();
+
+ /*
+ * TODO: somehow calling configure_sgx() in cpu_core_init() is not
+ * successful on the BSP (other threads are fine). Have to run it again
+ * here to get SGX enabled on BSP. This behavior needs to root-caused
+ * and we shall not have this redundant call.
+ */
+ configure_sgx(microcode_patch);
}
/* Ensure to re-program all MTRRs based on DRAM resource settings */
@@ -512,15 +527,25 @@ static void soc_post_cpus_init(void *unused)
int soc_skip_ucode_update(u32 current_patch_id, u32 new_patch_id)
{
- msr_t msr;
- /* If PRMRR/SGX is supported the FIT microcode load will set the msr
+ msr_t msr1;
+ msr_t msr2;
+
+ /*
+ * If PRMRR/SGX is supported the FIT microcode load will set the msr
* 0x08b with the Patch revision id one less than the id in the
* microcode binary. The PRMRR support is indicated in the MSR
- * MTRRCAP[12]. Check for this feature and avoid reloading the
- * same microcode during CPU initialization.
+ * MTRRCAP[12]. If SGX is not enabled, check and avoid reloading the
+ * same microcode during CPU initialization. If SGX is enabled, as
+ * part of SGX BIOS initialization steps, the same microcode needs to
+ * be reloaded after the core PRMRR MSRs are programmed.
*/
- msr = rdmsr(MTRR_CAP_MSR);
- return (msr.lo & PRMRR_SUPPORTED) && (current_patch_id == new_patch_id - 1);
+ msr1 = rdmsr(MTRR_CAP_MSR);
+ msr2 = rdmsr(PRMRR_PHYS_BASE_MSR);
+ if (msr2.lo && (current_patch_id == new_patch_id - 1))
+ return 0;
+ else
+ return (msr1.lo & PRMRR_SUPPORTED) &&
+ (current_patch_id == new_patch_id - 1);
}
/*
diff --git a/src/soc/intel/skylake/include/soc/cpu.h b/src/soc/intel/skylake/include/soc/cpu.h
index 33fb2f1..a259a2b 100644
--- a/src/soc/intel/skylake/include/soc/cpu.h
+++ b/src/soc/intel/skylake/include/soc/cpu.h
@@ -68,5 +68,6 @@ u32 cpu_family_model(void);
u32 cpu_stepping(void);
int cpu_is_ult(void);
int is_secondary_thread(void);
+void configure_sgx(const void *microcode_patch);
#endif
diff --git a/src/soc/intel/skylake/include/soc/msr.h b/src/soc/intel/skylake/include/soc/msr.h
index 4d295e1..4f05ba6 100644
--- a/src/soc/intel/skylake/include/soc/msr.h
+++ b/src/soc/intel/skylake/include/soc/msr.h
@@ -22,11 +22,13 @@
#define IA32_FEATURE_CONTROL 0x3a
#define CPUID_VMX (1 << 5)
#define CPUID_SMX (1 << 6)
+#define MSR_BIOS_UPGD_TRIG 0x7a
#define MSR_PLATFORM_INFO 0xce
#define PLATFORM_INFO_SET_TDP (1 << 29)
#define MSR_PMG_CST_CONFIG_CONTROL 0xe2
#define MSR_PMG_IO_CAPTURE_BASE 0xe4
#define MSR_FEATURE_CONFIG 0x13c
+#define IA32_MCG_CAP 0x179
#define SMM_MCA_CAP_MSR 0x17d
#define SMM_CPU_SVRSTR_BIT 57
#define SMM_CPU_SVRSTR_MASK (1 << (SMM_CPU_SVRSTR_BIT - 32))
@@ -48,13 +50,18 @@
#define ENERGY_POLICY_NORMAL 6
#define ENERGY_POLICY_POWERSAVE 15
#define IA32_PACKAGE_THERM_INTERRUPT 0x1b2
-#define EMRR_PHYS_BASE_MSR 0x1f4
-#define EMRR_PHYS_MASK_MSR 0x1f5
+#define PRMRR_PHYS_BASE_MSR 0x1f4
+#define PRMRR_PHYS_MASK_MSR 0x1f5
+#define PRMRR_PHYS_MASK_LOCK (1 << 10)
+#define PRMRR_PHYS_MASK_VALID (1 << 11)
#define IA32_PLATFORM_DCA_CAP 0x1f8
#define MSR_POWER_CTL 0x1fc
#define MSR_LT_LOCK_MEMORY 0x2e7
#define UNCORE_PRMRR_PHYS_BASE_MSR 0x2f4
#define UNCORE_PRMRR_PHYS_MASK_MSR 0x2f5
+#define MSR_SGX_OWNEREPOCH0 0x300
+#define MSR_SGX_OWNEREPOCH1 0x301
+#define IA32_MC0_CTL 0x400
#define IA32_MC0_STATUS 0x401
#define SMM_FEATURE_CONTROL_MSR 0x4e0
#define SMM_CPU_SAVE_EN (1 << 1)
diff --git a/src/soc/intel/skylake/sgx.c b/src/soc/intel/skylake/sgx.c
new file mode 100644
index 0000000..ba5455b
--- /dev/null
+++ b/src/soc/intel/skylake/sgx.c
@@ -0,0 +1,162 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <console/console.h>
+#include <chip.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/intel/microcode.h>
+#include <soc/cpu.h>
+#include <soc/msr.h>
+#include <soc/pci_devs.h>
+
+static int is_sgx_supported(void)
+{
+ struct cpuid_result cpuid_regs;
+ msr_t msr;
+
+ cpuid_regs = cpuid_ext(0x7, 0x0); /* EBX[2] is feature capability */
+ msr = rdmsr(MTRR_CAP_MSR); /* Bit 12 is PRMRR enablement */
+ return ((cpuid_regs.ebx & 0x4) && (msr.lo & PRMRR_SUPPORTED));
+}
+
+static int configure_core_prmrr(void)
+{
+ msr_t prmrr_base;
+ msr_t prmrr_mask;
+ msr_t msr;
+
+ /*
+ * PRMRR base and mask are read from the UNCORE PRMRR MSRs
+ * that are already set in FSP-M.
+ */
+ prmrr_base = rdmsr(UNCORE_PRMRR_PHYS_BASE_MSR);
+ prmrr_mask = rdmsr(UNCORE_PRMRR_PHYS_MASK_MSR);
+ if (!prmrr_base.lo) {
+ printk(BIOS_ERR, "SGX Error: Uncore PRMRR is not set!\n");
+ return -1;
+ }
+
+ msr = rdmsr(PRMRR_PHYS_MASK_MSR);
+ /* If it is locked don't attempt to write PRMRR MSRs. */
+ if (msr.lo & PRMRR_PHYS_MASK_LOCK)
+ return 0;
+
+ /* Program core PRMRR MSRs */
+ prmrr_base.lo |= 0x6; /* Set memory attribute to cache writeback */
+ wrmsr(PRMRR_PHYS_BASE_MSR, prmrr_base);
+ prmrr_mask.lo &= ~PRMRR_PHYS_MASK_VALID; /* Do not set the valid bit */
+ prmrr_mask.lo |= PRMRR_PHYS_MASK_LOCK; /* Lock it */
+ wrmsr(PRMRR_PHYS_MASK_MSR, prmrr_mask);
+ return 0;
+}
+
+static void enable_sgx(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(IA32_FEATURE_CONTROL);
+ /* Only enable it when it is not locked */
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= (1 << 18); /* Enable it */
+ wrmsr(IA32_FEATURE_CONTROL, msr);
+ }
+}
+
+static void lock_sgx(void)
+{
+ msr_t msr;
+
+ msr = rdmsr(IA32_FEATURE_CONTROL);
+ /* If it is locked don't attempt to lock it again. */
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= 1; /* Lock it */
+ wrmsr(IA32_FEATURE_CONTROL, msr);
+ }
+}
+
+static int owner_epoch_update(void)
+{
+ msr_t msr;
+
+ /*
+ * TODO - the Owner Epoch update mechanism is not determined yet
+ * For PoC just write '0's to the MSRs.
+ */
+ msr = {0, 0};
+ wrmsr(MSR_SGX_OWNEREPOCH0, msr);
+ wrmsr(MSR_SGX_OWNEREPOCH1, msr);
+ return 0;
+}
+
+static void activate_sgx(void)
+{
+ msr_t msr;
+
+ /*
+ * Activate SGX feature by writing 1b to MSR 0x7A on all threads.
+ * BIOS must ensure bit 0 is set prior to writing to it, then read it
+ * back and verify the bit is cleared to confirm SGX activation.
+ */
+ msr = rdmsr(MSR_BIOS_UPGD_TRIG);
+ if (msr.lo & 0x1) {
+ wrmsr(MSR_BIOS_UPGD_TRIG, (msr_t) {.lo = 0x1, .hi = 0});
+ /* Read back to verify it is activated */
+ msr = rdmsr(MSR_BIOS_UPGD_TRIG);
+ if (msr.lo & 0x1)
+ printk(BIOS_DEBUG, "SGX activation failed.\n");
+ else
+ printk(BIOS_DEBUG, "SGX activation was successful.\n");
+ } else {
+ printk(BIOS_DEBUG, "SGX feature is deactivated.\n");
+ }
+}
+
+void configure_sgx(const void *microcode_patch)
+{
+ device_t dev = SA_DEV_ROOT;
+ config_t *conf = dev->chip_info;
+ msr_t msr;
+
+ if (!conf->sgx_enable || !is_sgx_supported())
+ return;
+
+ /* Initialize PRMRR core MSRs */
+ if (configure_core_prmrr() < 0)
+ return;
+
+ /* Enable the SGX feature */
+ enable_sgx();
+
+ /* Update the owner epoch value */
+ if (owner_epoch_update() < 0)
+ return;
+
+ /* Ensure to lock memory before reload microcode patch */
+ msr = rdmsr(MSR_LT_LOCK_MEMORY);
+ if ((msr.lo & 1) == 0) {
+ msr.lo |= 1; /* Lock it */
+ wrmsr(MSR_LT_LOCK_MEMORY, msr);
+ }
+
+ /* Reload the microcode patch */
+ intel_microcode_load_unlocked(microcode_patch);
+
+ /* Lock the SGX feature */
+ lock_sgx();
+
+ /* Activate the SGX feature */
+ activate_sgx();
+}
1
0

Patch set updated for coreboot: intel/broadwell: Use the correct SATA port config for setting IOBP register
by Youness Alaoui Feb. 28, 2017
by Youness Alaoui Feb. 28, 2017
Feb. 28, 2017
Youness Alaoui (snifikino(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18514
-gerrit
commit ce388f059898697a1b747228289c55e52193f85c
Author: Youness Alaoui <kakaroto(a)kakaroto.homelinux.net>
Date: Mon Feb 27 12:03:39 2017 -0500
intel/broadwell: Use the correct SATA port config for setting IOBP register
Fix a typo that was introduce in change #18408.
https://review.coreboot.org/#/c/18408/
Setting one of the SATA port 3 IOBP setting was using the value from
the port 2 register.
On the purism/librem13 (on which SATA port 3 is tested), this change
doesn't seem to affect anything, as that typo wasn't exhibiting any
visible problems anyways.
Change-Id: I3948def5c0588791009c4b24cbc061552d9d1d48
Signed-off-by: Youness Alaoui <youness.alaoui(a)puri.sm>
---
src/soc/intel/broadwell/sata.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/soc/intel/broadwell/sata.c b/src/soc/intel/broadwell/sata.c
index 1f2e690..7b9bd0c 100644
--- a/src/soc/intel/broadwell/sata.c
+++ b/src/soc/intel/broadwell/sata.c
@@ -161,7 +161,7 @@ static void sata_init(struct device *dev)
pch_iobp_update(SATA_IOBP_SP3_SECRT88,
~(SATA_SECRT88_VADJ_MASK <<
SATA_SECRT88_VADJ_SHIFT),
- (config->sata_port2_gen3_tx &
+ (config->sata_port3_gen3_tx &
SATA_SECRT88_VADJ_MASK)
<< SATA_SECRT88_VADJ_SHIFT);
1
0

Patch set updated for coreboot: agesawrapper: Fix endless loop on bettong
by Ricardo Ribalda Delgado Feb. 28, 2017
by Ricardo Ribalda Delgado Feb. 28, 2017
Feb. 28, 2017
Ricardo Ribalda Delgado (ricardo.ribalda(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17924
-gerrit
commit 1fe9849a2b4ce09f397c5e636c930611fed03069
Author: Ricardo Ribalda Delgado <ricardo.ribalda(a)gmail.com>
Date: Tue Dec 20 10:08:45 2016 +0100
agesawrapper: Fix endless loop on bettong
AGESA AmdInitEarly() reconfigures the lapic timer in a way that
conflicts with lapic/apic_timer.
This results in an endless loop when printk() is called after
AmdInitEarly() and before the apic_timer is initialized.
This patch forces a reconfiguration of the timer after
AmdInitEarly() is called.
Codepath of the endless loop:
printk()->
(...)->
uart_tx_byte->
uart8250_mem_tx_byte->
udelay()->
start = lapic_read(LAPIC_TMCCT);
do {
value = lapic_read(LAPIC_TMCCT);
} while ((start - value) < ticks);
[lapic_read returns the same value after AmdInitEarly()]
Change-Id: I1a08789c89401b2bf6d11846ad7c376bfc68801b
Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda(a)gmail.com>
---
src/northbridge/amd/pi/agesawrapper.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/northbridge/amd/pi/agesawrapper.c b/src/northbridge/amd/pi/agesawrapper.c
index 0fe8eab..2e79f47 100644
--- a/src/northbridge/amd/pi/agesawrapper.c
+++ b/src/northbridge/amd/pi/agesawrapper.c
@@ -15,6 +15,7 @@
#include <AGESA.h>
#include <cbfs.h>
+#include <delay.h>
#include <cpu/amd/pi/s3_resume.h>
#include <cpu/x86/mtrr.h>
#include <cpuRegisters.h>
@@ -107,6 +108,12 @@ AGESA_STATUS agesawrapper_amdinitearly(void)
AmdEarlyParamsPtr->GnbConfig.PsppPolicy = PsppDisabled;
status = AmdInitEarly ((AMD_EARLY_PARAMS *)AmdParamStruct.NewStructPtr);
+ /*
+ * init_timer() needs to be called on CZ PI, because AGESA resets the LAPIC reload value
+ * on the AMD_INIT_EARLY call
+ */
+ if (IS_ENABLED(CONFIG_CPU_AMD_PI_00660F01))
+ init_timer();
if (status != AGESA_SUCCESS) agesawrapper_amdreadeventlog(AmdParamStruct.StdHeader.HeapStatus);
AmdReleaseStruct (&AmdParamStruct);
1
0

New patch to review for coreboot: ec/lenovo/h8: Replace `<<` by older compatible syntax
by Paul Menzel Feb. 28, 2017
by Paul Menzel Feb. 28, 2017
Feb. 28, 2017
Paul Menzel (paulepanter(a)users.sourceforge.net) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18520
-gerrit
commit ed462634b2014473e2cd0b25e00cf97f763e5d58
Author: Paul Menzel <paulepanter(a)users.sourceforge.net>
Date: Tue Feb 28 20:23:37 2017 +0100
ec/lenovo/h8: Replace `<<` by older compatible syntax
```
Intel ACPI Component Architecture
ASL Optimizing Compiler version 20140926-32 [Oct 1 2014]
Copyright (c) 2000 - 2014 Intel Corporation
Compiler aborting due to parser-detected syntax error(s)
dsdt.aml 1698: Return (TBSW << 3)
Error 6126 - ^ Invalid character (0x3C), expecting ASL keyword or name
Store (DHKN, EMSK)
dsdt.aml 1698: Return (TBSW << 3)
Error 6126 - ^ Invalid character (0x3C), expecting ASL keyword or name
dsdt.aml 1698: Return (TBSW << 3)
Error 6126 - ^ syntax error, unexpected PARSEOP_INTEGER, expecting ')'
ASL Input: dsdt.aml - 2405 lines, 42862 bytes, 1000 keywords
Compilation complete. 3 Errors, 0 Warnings, 0 Remarks, 0 Optimizations
```
Change-Id: Id7e309c31612387da3920cf7d846b358ac2bdc71
Signed-off-by: Paul Menzel <paulepanter(a)users.sourceforge.net>
---
src/ec/lenovo/h8/acpi/ec.asl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ec/lenovo/h8/acpi/ec.asl b/src/ec/lenovo/h8/acpi/ec.asl
index b8350ad..ed62afe 100644
--- a/src/ec/lenovo/h8/acpi/ec.asl
+++ b/src/ec/lenovo/h8/acpi/ec.asl
@@ -363,7 +363,7 @@ Device(EC)
/* Report tablet mode switch state */
Method (MHKG, 0, NotSerialized)
{
- Return (TBSW << 3)
+ Return (ShiftLeft(TBSW, 3))
}
/* Mute audio */
Method (SSMS, 1, NotSerialized)
1
0

Patch set updated for coreboot: nb/intel/i945/raminit.c: Refactor tRD selection
by Arthur Heymans Feb. 28, 2017
by Arthur Heymans Feb. 28, 2017
Feb. 28, 2017
Arthur Heymans (arthur(a)aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18354
-gerrit
commit 14cb0bbaae815cdfcad9933196260da6a3b45d4d
Author: Arthur Heymans <arthur(a)aheymans.xyz>
Date: Sun Feb 12 23:34:39 2017 +0100
nb/intel/i945/raminit.c: Refactor tRD selection
Inspired by gm45 code, which sets this value the same way.
Some values for tRD on 800 and 1067MHz FSB were set wrong because the
CAS/Freq selection was wrong.
PASSED memtest86+ during 10h+ on 1067MHZ fsb with 667MHz ddr2, CAS 5
Change-Id: I8002daf25b7603131b78b01075f43fd23747dd94
Signed-off-by: Arthur Heymans <arthur(a)aheymans.xyz>
---
src/northbridge/intel/i945/raminit.c | 49 ++++++++++--------------------------
1 file changed, 13 insertions(+), 36 deletions(-)
diff --git a/src/northbridge/intel/i945/raminit.c b/src/northbridge/intel/i945/raminit.c
index cc227cc..68d93eb 100644
--- a/src/northbridge/intel/i945/raminit.c
+++ b/src/northbridge/intel/i945/raminit.c
@@ -1720,21 +1720,11 @@ static void sdram_program_odt_tristate(struct sys_info *sysinfo)
static void sdram_set_timing_and_control(struct sys_info *sysinfo)
{
- u32 reg32, off32;
+ u32 reg32, tRD_min;
u32 tWTR;
u32 temp_drt;
int i, page_size;
- static const u8 drt0_table[] = {
- /* CL 3, 4, 5 */
- 3, 4, 5, /* FSB533, DDR667/533/400 */
- 4, 5, 6, /* FSB667, DDR667/533/400 */
- 5, 6, 7, /* FSB800, DDR400/533 */
- 6, 7, 8, /* FSB800, DDR667 */
- 5, 6, 7, /* FSB1066, DDR400 */
- 7, 8, 9, /* FSB1066, DDR533/DDR667 */
- };
-
static const u8 cas_table[] = {
2, 1, 0, 3
};
@@ -1787,34 +1777,21 @@ static void sdram_set_timing_and_control(struct sys_info *sysinfo)
/* CxDRT0 [23:22], [21:20], [19:18] [16] have fixed values */
temp_drt |= ( (1 << 22) | (3 << 20) | (1 << 18) | (0 << 16) );
- /* Program Write Auto Precharge to Activate */
- off32 = 0;
+ /*
+ * tRD is the delay the memory controller is waiting on the FSB,
+ * in mclk domain.
+ * This parameter is important for stability and performance.
+ * Those values might not be optimal but seem stable.
+ */
+ tRD_min = sysinfo->cas;
switch (sysinfo->fsb_frequency) {
- case 533:
- off32 = 0;
- break;
- case 667:
- off32 = 3;
- break;
- case 800:
- if (sysinfo->memory_frequency <= 533) {
- off32 = 6;
- break;
- }
- off32 = 9;
- break;
- case 1066:
- if (sysinfo->memory_frequency == 400) {
- off32 = 12;
- break;
- }
- off32 = 15;
- break;
+ case 533: break;
+ case 667: tRD_min += 1; break;
+ case 800: tRD_min += 2; break;
+ case 1066: tRD_min += 3; break;
}
- off32 += sysinfo->cas - 3;
- reg32 = drt0_table[off32];
- temp_drt |= (reg32 << 11);
+ temp_drt |= (tRD_min << 11);
/* Read Auto Precharge to Activate */
1
0

Patch set updated for coreboot: nb/i945/raminit: Use common ddr2 decode functions
by Arthur Heymans Feb. 28, 2017
by Arthur Heymans Feb. 28, 2017
Feb. 28, 2017
Arthur Heymans (arthur(a)aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18305
-gerrit
commit 1eb9e8c4e1e8ee0e548923826ddede6364ae0ea2
Author: Arthur Heymans <arthur(a)aheymans.xyz>
Date: Mon Feb 6 22:40:14 2017 +0100
nb/i945/raminit: Use common ddr2 decode functions
This simplifies computing dram timings a lot.
This removes computation of rank size based on columns, rows,
banks,... and uses the information in SPD byte 31.
The result of this is that dimms with multiple asymmetric ranks are
not supported anymore. These however are very rare and most likely
never tested on this platform.
Change-Id: I97c93939d11807752797785dd88c70b43a236ee3
Signed-off-by: Arthur Heymans <arthur(a)aheymans.xyz>
---
src/northbridge/intel/i945/raminit.c | 614 +++++++----------------------------
src/northbridge/intel/i945/raminit.h | 16 +-
2 files changed, 121 insertions(+), 509 deletions(-)
diff --git a/src/northbridge/intel/i945/raminit.c b/src/northbridge/intel/i945/raminit.c
index 68d93eb..22e1476 100644
--- a/src/northbridge/intel/i945/raminit.c
+++ b/src/northbridge/intel/i945/raminit.c
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2017 Arthur Heymans <arthur(a)aheymans.xyz>
*
* 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
@@ -28,6 +29,7 @@
#include "i945.h"
#include "chip.h"
#include <cbmem.h>
+#include <device/dram/ddr2.h>
/* Debugging macros. */
#if CONFIG_DEBUG_RAM_SETUP
@@ -315,8 +317,6 @@ static void sdram_detect_errors(struct sys_info *sysinfo)
static void sdram_get_dram_configuration(struct sys_info *sysinfo)
{
u32 dimm_mask = 0;
- int i;
-
/**
* i945 supports two DIMMs, in two configurations:
*
@@ -357,9 +357,11 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
*
*/
- for (i = 0; i<(2 * DIMM_SOCKETS); i++) {
+ int i, j, spd_size;
+ spd_raw_data raw_spd;
+
+ for (i = 0; i < (2 * DIMM_SOCKETS); i++) {
int device = get_dimm_spd_address(sysinfo, i);
- u8 reg8;
/* Initialize the socket information with a sane value */
sysinfo->dimm[i] = SYSINFO_DIMM_NOT_POPULATED;
@@ -368,31 +370,40 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
if (!sdram_capabilities_dual_channel() && (i >> 1))
continue;
- printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: ", (i >> 1), (i & 1));
-
if (spd_read_byte(device, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR2) {
- printk(BIOS_DEBUG, "N/A\n");
+ printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: N/A\n", (i >> 1), (i & 1));
continue;
}
- reg8 = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE);
- if (reg8 == ERROR_SCHEME_ECC)
- die("Error: ECC memory not supported by this chipset\n");
+ spd_size = spd_decode_spd_size_ddr2(spd_read_byte(device, 0));
+ if (spd_size > sizeof(raw_spd))
+ continue;
+
+ for (j = 0; j < spd_size; j++)
+ raw_spd[j] = spd_read_byte(device, j);
+
+ if (spd_decode_ddr2(&sysinfo->dimm_info[i], raw_spd)
+ != SPD_STATUS_OK)
+ continue;
+
+ if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP))
+ dram_print_spd_ddr2(&sysinfo->dimm_info[i]);
+
+ if (sysinfo->dimm_info[i].flags.is_ecc)
+ die("\nError: ECC memory not supported by this chipset\n");
- reg8 = spd_read_byte(device, SPD_MODULE_ATTRIBUTES);
- if (reg8 & MODULE_BUFFERED)
- die("Error: Buffered memory not supported by this chipset\n");
- if (reg8 & MODULE_REGISTERED)
- die("Error: Registered memory not supported by this chipset\n");
+ if (spd_dimm_is_registered_ddr2(sysinfo->dimm_info[i].dimm_type))
+ die("\nError: Registered memory not supported by this chipset\n");
- switch (spd_read_byte(device, SPD_PRIMARY_SDRAM_WIDTH)) {
+ printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: ", (i >> 1), (i & 1));
+ switch (sysinfo->dimm_info[i].width) {
case 0x08:
- switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
- case 1:
+ switch (sysinfo->dimm_info[i].ranks) {
+ case 2:
printk(BIOS_DEBUG, "x8DDS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X8DDS;
break;
- case 0:
+ case 1:
printk(BIOS_DEBUG, "x8DS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X8DS;
break;
@@ -401,12 +412,12 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
}
break;
case 0x10:
- switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
- case 1:
+ switch (sysinfo->dimm_info[i].ranks) {
+ case 2:
printk(BIOS_DEBUG, "x16DS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X16DS;
break;
- case 0:
+ case 1:
printk(BIOS_DEBUG, "x16SS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X16SS;
break;
@@ -417,15 +428,14 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
default:
die("Unsupported DDR-II memory width.\n");
}
-
dimm_mask |= (1 << i);
}
-
if (!dimm_mask) {
die("No memory installed.\n");
}
if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) {
+ /* Possibly does not boot in this case */
printk(BIOS_INFO, "Channel 0 has no memory populated.\n");
}
}
@@ -438,7 +448,6 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
static void sdram_verify_package_type(struct sys_info * sysinfo)
{
int i;
-
/* Assume no stacked DIMMs are available until we find one */
sysinfo->package = 0;
for (i = 0; i < 2*DIMM_SOCKETS; i++) {
@@ -446,365 +455,86 @@ static void sdram_verify_package_type(struct sys_info * sysinfo)
continue;
/* Is the current DIMM a stacked DIMM? */
- if (spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_NUM_DIMM_BANKS) & (1 << 4))
+ if (sysinfo->dimm_info[i].flags.stacked)
sysinfo->package = 1;
}
}
-static u8 sdram_possible_cas_latencies(struct sys_info * sysinfo)
+static void select_cas_dramfreq(struct sys_info * sysinfo)
{
- int i;
- u8 cas_mask;
-
- /* Setup CAS mask with all supported CAS Latencies */
- cas_mask = SPD_CAS_LATENCY_DDR2_3 |
- SPD_CAS_LATENCY_DDR2_4 |
- SPD_CAS_LATENCY_DDR2_5;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- if (sysinfo->dimm[i] != SYSINFO_DIMM_NOT_POPULATED)
- cas_mask &= spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_ACCEPTABLE_CAS_LATENCIES);
- }
-
- if (!cas_mask) {
- die("No DDR-II modules with accepted CAS latencies found.\n");
- }
-
- return cas_mask;
-}
-
-static void sdram_detect_cas_latency_and_ram_speed(struct sys_info * sysinfo, u8 cas_mask)
-{
- int i, j, idx;
- int lowest_common_cas = 0;
- int max_ram_speed = 0;
-
- const u8 ddr2_speeds_table[] = {
- 0x50, 0x60, /* DDR2 400: tCLK = 5.0ns tAC = 0.6ns */
- 0x3d, 0x50, /* DDR2 533: tCLK = 3.75ns tAC = 0.5ns */
- 0x30, 0x45, /* DDR2 667: tCLK = 3.0ns tAC = 0.45ns */
- };
-
- const u8 spd_lookup_table[] = {
- SPD_MIN_CYCLE_TIME_AT_CAS_MAX, SPD_ACCESS_TIME_FROM_CLOCK,
- SPD_SDRAM_CYCLE_TIME_2ND, SPD_ACCESS_TIME_FROM_CLOCK_2ND,
- SPD_SDRAM_CYCLE_TIME_3RD, SPD_ACCESS_TIME_FROM_CLOCK_3RD
- };
+ u8 selected_cas;
+ u8 cas_mask = SPD_CAS_LATENCY_DDR2_3 | SPD_CAS_LATENCY_DDR2_4
+ | SPD_CAS_LATENCY_DDR2_5;
+ u32 common_tCLK, min_tCLK = TCK_200MHZ;
switch (sdram_capabilities_max_supported_memory_frequency()) {
- case 400: max_ram_speed = 0; break;
- case 533: max_ram_speed = 1; break;
- case 667: max_ram_speed = 2; break;
- }
-
- sysinfo->memory_frequency = 0;
- sysinfo->cas = 0;
-
- if (cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- lowest_common_cas = 3;
- } else if (cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- lowest_common_cas = 4;
- } else if (cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- lowest_common_cas = 5;
+ case 400: min_tCLK = TCK_200MHZ; break;
+ case 533: min_tCLK = TCK_266MHZ; break;
+ case 667: min_tCLK = TCK_333MHZ; break;
}
- PRINTK_DEBUG("lowest common cas = %d\n", lowest_common_cas);
- for (j = max_ram_speed; j>=0; j--) {
- int freq_cas_mask = cas_mask;
-
- PRINTK_DEBUG("Probing Speed %d\n", j);
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- int device = get_dimm_spd_address(sysinfo, i);
- int current_cas_mask;
-
- PRINTK_DEBUG(" DIMM: %d\n", i);
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
- continue;
- }
-
- current_cas_mask = spd_read_byte(device, SPD_ACCEPTABLE_CAS_LATENCIES);
-
- while (current_cas_mask) {
- int highest_supported_cas = 0, current_cas = 0;
- PRINTK_DEBUG(" Current CAS mask: %04x; ", current_cas_mask);
- if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- highest_supported_cas = 5;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- highest_supported_cas = 4;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- highest_supported_cas = 3;
- } else {
- die("Invalid max. CAS.\n");
- }
- if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- current_cas = 3;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- current_cas = 4;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- current_cas = 5;
- } else {
- die("Invalid CAS.\n");
- }
-
- idx = highest_supported_cas - current_cas;
- PRINTK_DEBUG("idx=%d, ", idx);
- PRINTK_DEBUG("tCLK=%x, ", spd_read_byte(device, spd_lookup_table[2*idx]));
- PRINTK_DEBUG("tAC=%x", spd_read_byte(device, spd_lookup_table[(2*idx)+1]));
-
- if (spd_read_byte(device, spd_lookup_table[2*idx]) <= ddr2_speeds_table[2*j] &&
- spd_read_byte(device, spd_lookup_table[(2*idx)+1]) <= ddr2_speeds_table[(2*j)+1]) {
- PRINTK_DEBUG(": OK\n");
- break;
- }
-
- PRINTK_DEBUG(": Not fast enough!\n");
-
- current_cas_mask &= ~(1 << (current_cas));
- }
-
- freq_cas_mask &= current_cas_mask;
- if (!current_cas_mask) {
- PRINTK_DEBUG(" No valid CAS for this speed on DIMM %d\n", i);
- break;
- }
- }
- PRINTK_DEBUG(" freq_cas_mask for speed %d: %04x\n", j, freq_cas_mask);
- if (freq_cas_mask) {
- switch (j) {
- case 0: sysinfo->memory_frequency = 400; break;
- case 1: sysinfo->memory_frequency = 533; break;
- case 2: sysinfo->memory_frequency = 667; break;
- }
- if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- sysinfo->cas = 3;
- } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- sysinfo->cas = 4;
- } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- sysinfo->cas = 5;
- }
- break;
- }
- }
-
- if (sysinfo->memory_frequency && sysinfo->cas) {
- printk(BIOS_DEBUG, "Memory will be driven at %dMHz with CAS=%d clocks\n",
- sysinfo->memory_frequency, sysinfo->cas);
- } else {
+ common_tCLK = get_common_freq_cas(cas_mask, sysinfo->dimm_info,
+ 2 * DIMM_SOCKETS, &selected_cas);
+ if (common_tCLK == 0)
die("Could not find common memory frequency and CAS\n");
- }
-}
-
-static void sdram_detect_smallest_tRAS(struct sys_info * sysinfo)
-{
- int i;
- int tRAS_time;
- int tRAS_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRAS_cycles = 4; /* 4 clocks minimum */
- tRAS_time = tRAS_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY);
- if (!reg8) {
- die("Invalid tRAS value.\n");
- }
-
- while ((tRAS_time >> 2) < reg8) {
- tRAS_time += freq_multiplier;
- tRAS_cycles++;
- }
- }
- if (tRAS_cycles > 0x18) {
- die("DDR-II Module does not support this frequency (tRAS error)\n");
- }
-
- printk(BIOS_DEBUG, "tRAS = %d cycles\n", tRAS_cycles);
- sysinfo->tras = tRAS_cycles;
-}
-
-static void sdram_detect_smallest_tRP(struct sys_info * sysinfo)
-{
- int i;
- int tRP_time;
- int tRP_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRP_cycles = 2; /* 2 clocks minimum */
- tRP_time = tRP_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
+ common_tCLK = MAX(common_tCLK, min_tCLK);
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ROW_PRECHARGE_TIME);
- if (!reg8) {
- die("Invalid tRP value.\n");
- }
-
- while (tRP_time < reg8) {
- tRP_time += freq_multiplier;
- tRP_cycles++;
- }
- }
+ sysinfo->cas = selected_cas;
+ sysinfo->tclk = common_tCLK;
- if (tRP_cycles > 6) {
- die("DDR-II Module does not support this frequency (tRP error)\n");
+ switch (sysinfo->tclk) {
+ case TCK_200MHZ: sysinfo->memory_frequency = 400; break;
+ case TCK_266MHZ: sysinfo->memory_frequency = 533; break;
+ case TCK_333MHZ: sysinfo->memory_frequency = 667; break;
}
-
- printk(BIOS_DEBUG, "tRP = %d cycles\n", tRP_cycles);
- sysinfo->trp = tRP_cycles;
+ printk(BIOS_DEBUG, "Memory will be driven at %dMHz with CAS=%d clocks\n",
+ sysinfo->memory_frequency, sysinfo->cas);
}
-static void sdram_detect_smallest_tRCD(struct sys_info * sysinfo)
+static void select_dram_timings(struct sys_info * sysinfo)
{
int i;
- int tRCD_time;
- int tRCD_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRCD_cycles = 2; /* 2 clocks minimum */
- tRCD_time = tRCD_cycles * freq_multiplier;
+ int tCLK = sysinfo->tclk;
+ int tRAS_cycles, tRP_cycles, tRCD_cycles, tWR_cycles, tRFC_cycles;
for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_RAS_TO_CAS_DELAY);
- if (!reg8) {
- die("Invalid tRCD value.\n");
- }
-
- while (tRCD_time < reg8) {
- tRCD_time += freq_multiplier;
- tRCD_cycles++;
- }
- }
- if (tRCD_cycles > 6) {
- die("DDR-II Module does not support this frequency (tRCD error)\n");
- }
-
- printk(BIOS_DEBUG, "tRCD = %d cycles\n", tRCD_cycles);
- sysinfo->trcd = tRCD_cycles;
-}
-
-static void sdram_detect_smallest_tWR(struct sys_info * sysinfo)
-{
- int i;
- int tWR_time;
- int tWR_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tWR_cycles = 2; /* 2 clocks minimum */
- tWR_time = tWR_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_WRITE_RECOVERY_TIME);
- if (!reg8) {
- die("Invalid tWR value.\n");
- }
-
- while (tWR_time < reg8) {
- tWR_time += freq_multiplier;
- tWR_cycles++;
- }
- }
- if (tWR_cycles > 5) {
- die("DDR-II Module does not support this frequency (tWR error)\n");
- }
-
- printk(BIOS_DEBUG, "tWR = %d cycles\n", tWR_cycles);
- sysinfo->twr = tWR_cycles;
-}
-
-static void sdram_detect_smallest_tRFC(struct sys_info * sysinfo)
-{
- int i, index = 0;
-
- const u8 tRFC_cycles[] = {
- /* 75 105 127.5 */
- 15, 21, 26, /* DDR2-400 */
- 20, 28, 34, /* DDR2-533 */
- 25, 35, 43 /* DDR2-667 */
- };
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = sysinfo->banksize[i*2];
- switch (reg8) {
- case 0x04: reg8 = 0; break;
- case 0x08: reg8 = 1; break;
- case 0x10: reg8 = 2; break;
- case 0x20: reg8 = 3; break;
- }
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_X16DS || sysinfo->dimm[i] == SYSINFO_DIMM_X16SS)
- reg8++;
-
- if (reg8 > 3) {
- /* Can this happen? Go back to 127.5ns just to be sure
- * we don't run out of the array. This may be wrong
- */
- printk(BIOS_DEBUG, "DIMM %d is 1Gb x16.. Please report.\n", i);
- reg8 = 3;
- }
-
- if (reg8 > index)
- index = reg8;
-
- }
- index--;
- switch (sysinfo->memory_frequency) {
- case 667: index += 3; /* Fallthrough */
- case 533: index += 3; /* Fallthrough */
- case 400: break;
- }
-
- sysinfo->trfc = tRFC_cycles[index];
- printk(BIOS_DEBUG, "tRFC = %d cycles\n", tRFC_cycles[index]);
+ tRAS_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRAS, tCLK);
+ if (tRAS_cycles > 0x18)
+ die("DDR-II Module does not support this frequency (tRAS error)\n");
+ if (tRAS_cycles > sysinfo->tras)
+ sysinfo->tras = tRAS_cycles;
+
+ tRP_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRP, tCLK);
+ if (tRP_cycles > 6)
+ die("DDR-II Module does not support this frequency (tRP error)\n");
+ if (tRP_cycles > sysinfo->trp)
+ sysinfo->trp = tRP_cycles;
+
+ tRCD_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRCD, tCLK);
+ if (tRCD_cycles > 6)
+ die("DDR-II Module does not support this frequency (tRCD error)\n");
+ if (tRCD_cycles > sysinfo->trcd)
+ sysinfo->trcd = tRCD_cycles;
+
+ tWR_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tWR, tCLK);
+ if (tWR_cycles > 5)
+ die("DDR-II Module does not support this frequency (tWR error)\n");
+ if (tWR_cycles > sysinfo->twr)
+ sysinfo->twr = tWR_cycles;
+
+ tRFC_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRFC, tCLK);
+ if (tRFC_cycles > sysinfo->trfc)
+ sysinfo->trfc = tRFC_cycles;
+
+ }
+ printk(BIOS_DEBUG, "tRAS = %d cycles\n", sysinfo->tras);
+ printk(BIOS_DEBUG, "tRP = %d cycles\n", sysinfo->trp);
+ printk(BIOS_DEBUG, "tRCD = %d cycles\n", sysinfo->trcd);
+ printk(BIOS_DEBUG, "tWR = %d cycles\n", sysinfo->twr);
+ printk(BIOS_DEBUG, "tRFC = %d cycles\n", sysinfo->trfc);
}
static void sdram_detect_smallest_refresh(struct sys_info * sysinfo)
@@ -819,23 +549,20 @@ static void sdram_detect_smallest_refresh(struct sys_info * sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- refresh = spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_REFRESH) & ~(1 << 7);
-
- /* 15.6us */
- if (!refresh)
- continue;
+ refresh = sysinfo->dimm_info[i].tRR;
/* Refresh is slower than 15.6us, use 15.6us */
- if (refresh > 2)
+ /* tRR is decoded in units of 1/256us */
+ if (refresh >= 15625 * 256)
continue;
- if (refresh == 2) {
+ if (refresh == 15625 * 256 / 2) {
sysinfo->refresh = 1;
break;
}
- die("DDR-II module has unsupported refresh value\n");
+ if (refresh == 15625 * 256 / 4)
+ die("DDR-II module has unsupported refresh value\n");
}
printk(BIOS_DEBUG, "Refresh: %s\n", sysinfo->refresh?"7.8us":"15.6us");
}
@@ -848,8 +575,7 @@ static void sdram_verify_burst_length(struct sys_info * sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- if (!(spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_SUPPORTED_BURST_LENGTHS) & SPD_BURST_LENGTH_8))
+ if (!sysinfo->dimm_info[i].flags.bl8)
die("Only DDR-II RAM with burst length 8 is supported by this chipset.\n");
}
}
@@ -1376,121 +1102,26 @@ static void sdram_enable_system_memory_io(struct sys_info *sysinfo)
}
}
-struct dimm_size {
- unsigned long side1;
- unsigned long side2;
-};
-
-static struct dimm_size sdram_get_dimm_size(struct sys_info *sysinfo, u16 dimmno)
-{
- /* Calculate the log base 2 size of a DIMM in bits */
- struct dimm_size sz;
- int value, low, rows, columns, device;
-
- device = get_dimm_spd_address(sysinfo, dimmno);
- sz.side1 = 0;
- sz.side2 = 0;
-
- rows = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
- if (rows < 0) goto hw_err;
- if ((rows & 0xf) == 0) goto val_err;
- sz.side1 += rows & 0xf;
-
- columns = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
- if (columns < 0) goto hw_err;
- if ((columns & 0xf) == 0) goto val_err;
- sz.side1 += columns & 0xf;
-
- value = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM); /* banks */
- if (value < 0) goto hw_err;
- if ((value & 0xff) == 0) goto val_err;
- sz.side1 += log2(value & 0xff);
-
- /* Get the module data width and convert it to a power of two */
- value = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_MSB); /* (high byte) */
- if (value < 0) goto hw_err;
- value &= 0xff;
- value <<= 8;
-
- low = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB); /* (low byte) */
- if (low < 0) goto hw_err;
- value = value | (low & 0xff);
- if ((value != 72) && (value != 64)) goto val_err;
- sz.side1 += log2(value);
-
- /* side 2 */
- value = spd_read_byte(device, SPD_NUM_DIMM_BANKS); /* number of physical banks */
-
- if (value < 0) goto hw_err;
- value &= 7;
- value++;
- if (value == 1) goto out;
- if (value != 2) goto val_err;
-
- /* Start with the symmetrical case */
- sz.side2 = sz.side1;
-
- if ((rows & 0xf0) == 0) goto out; /* If symmetrical we are done */
-
- /* Don't die here, I have not come across any of these to test what
- * actually happens.
- */
- printk(BIOS_ERR, "Asymmetric DIMMs are not supported by this chipset\n");
-
- sz.side2 -= (rows & 0x0f); /* Subtract out rows on side 1 */
- sz.side2 += ((rows >> 4) & 0x0f); /* Add in rows on side 2 */
-
- sz.side2 -= (columns & 0x0f); /* Subtract out columns on side 1 */
- sz.side2 += ((columns >> 4) & 0x0f); /* Add in columns on side 2 */
-
- goto out;
-
- val_err:
- die("Bad SPD value\n");
- hw_err:
- /* If a hardware error occurs the spd ROM probably does not exist.
- * In this case report that there is no memory
- */
- sz.side1 = 0;
- sz.side2 = 0;
-out:
- return sz;
-}
-
static void sdram_detect_dimm_size(struct sys_info * sysinfo)
{
int i;
for (i = 0; i < 2 * DIMM_SOCKETS; i++) {
- struct dimm_size sz;
-
sysinfo->banksize[i * 2] = 0;
sysinfo->banksize[(i * 2) + 1] = 0;
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
-
- sz = sdram_get_dimm_size(sysinfo, i);
-
- sysinfo->banks[i] = spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_NUM_BANKS_PER_SDRAM); /* banks */
-
- if (sz.side1 < 30)
+ if (sysinfo->dimm_info[i].ranksize_mb < 128)
die("DDR-II rank size smaller than 128MB is not supported.\n");
- sysinfo->banksize[i * 2] = 1 << (sz.side1 - 28);
-
+ sysinfo->banksize[i * 2] = sysinfo->dimm_info[i].ranksize_mb / 32;
printk(BIOS_DEBUG, "DIMM %d side 0 = %d MB\n", i, sysinfo->banksize[i * 2] * 32 );
- if (!sz.side2)
+ if (sysinfo->dimm_info[i].ranks == 1)
continue;
- /* If there is a second side, it has to have at least 128M, too */
- if (sz.side2 < 30)
- die("DDR-II rank size smaller than 128MB is not supported.\n");
-
- sysinfo->banksize[(i * 2) + 1] = 1 << (sz.side2 - 28);
-
+ sysinfo->banksize[(i * 2) + 1] = sysinfo->dimm_info[i].ranksize_mb / 32;
printk(BIOS_DEBUG, "DIMM %d side 1 = %d MB\n", i, sysinfo->banksize[(i * 2) + 1] * 32);
}
}
@@ -1564,25 +1195,19 @@ static int sdram_program_row_boundaries(struct sys_info *sysinfo)
static int sdram_set_row_attributes(struct sys_info *sysinfo)
{
- int i, value;
+ int i;
u16 dra0 = 0, dra1 = 0, dra = 0;
printk(BIOS_DEBUG, "Setting row attributes...\n");
for (i = 0; i < 2 * DIMM_SOCKETS; i++) {
- u16 device;
u8 columnsrows;
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
continue;
}
- device = get_dimm_spd_address(sysinfo, i);
-
- value = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
- columnsrows = (value & 0x0f);
-
- value = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
- columnsrows |= (value & 0xf) << 4;
+ columnsrows = (sysinfo->dimm_info[i].row_bits & 0x0f)
+ | (sysinfo->dimm_info[i].col_bits & 0xf) << 4;
switch (columnsrows) {
case 0x9d: dra = 2; break;
@@ -1630,7 +1255,7 @@ static void sdram_set_bank_architecture(struct sys_info *sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- if (sysinfo->banks[i] != 8)
+ if (sysinfo->dimm_info[i].banks != 8)
continue;
printk(BIOS_SPEW, "DIMM%d has 8 banks.\n", i);
@@ -3039,7 +2664,7 @@ static void sdram_setup_processor_side(void)
void sdram_initialize(int boot_path, const u8 *spd_addresses)
{
struct sys_info sysinfo;
- u8 reg8, cas_mask;
+ u8 reg8;
printk(BIOS_DEBUG, "Setting up RAM controller.\n");
@@ -3057,20 +2682,11 @@ void sdram_initialize(int boot_path, const u8 *spd_addresses)
/* Check whether we have stacked DIMMs */
sdram_verify_package_type(&sysinfo);
- /* Determine common CAS */
- cas_mask = sdram_possible_cas_latencies(&sysinfo);
+ /* Select common CAS latency and dram frequency */
+ select_cas_dramfreq(&sysinfo);
- /* Choose Common Frequency */
- sdram_detect_cas_latency_and_ram_speed(&sysinfo, cas_mask);
-
- /* Determine smallest common tRAS */
- sdram_detect_smallest_tRAS(&sysinfo);
-
- /* Determine tRP */
- sdram_detect_smallest_tRP(&sysinfo);
-
- /* Determine tRCD */
- sdram_detect_smallest_tRCD(&sysinfo);
+ /* Select nRAS, nRP, nRCD, nWR, tRFC */
+ select_dram_timings(&sysinfo);
/* Determine smallest refresh period */
sdram_detect_smallest_refresh(&sysinfo);
@@ -3078,15 +2694,9 @@ void sdram_initialize(int boot_path, const u8 *spd_addresses)
/* Verify all DIMMs support burst length 8 */
sdram_verify_burst_length(&sysinfo);
- /* determine tWR */
- sdram_detect_smallest_tWR(&sysinfo);
-
/* Determine DIMM size parameters (rows, columns banks) */
sdram_detect_dimm_size(&sysinfo);
- /* determine tRFC */
- sdram_detect_smallest_tRFC(&sysinfo);
-
/* Program PLL settings */
sdram_program_pll_settings(&sysinfo);
diff --git a/src/northbridge/intel/i945/raminit.h b/src/northbridge/intel/i945/raminit.h
index 0554900..965998a 100644
--- a/src/northbridge/intel/i945/raminit.h
+++ b/src/northbridge/intel/i945/raminit.h
@@ -23,15 +23,18 @@
/* Burst length is always 8 */
#define BURSTLENGTH 8
+#include <device/dram/ddr2.h>
+
struct sys_info {
u16 memory_frequency; /* 400, 533 or 667 */
u16 fsb_frequency; /* 945GM: 400, 533 or 667 / 945GC: 533, 800, or 1066 */
+ u32 tclk;
- u8 trp; /* calculated by sdram_detect_smallest_tRP() */
- u8 trcd; /* calculated by sdram_detect_smallest_tRCD() */
- u8 tras; /* calculated by sdram_detect_smallest_tRAS() */
- u8 trfc; /* calculated by sdram_detect_smallest_tRFC() */
- u8 twr; /* calculated by sdram_detect_smallest_tWR() */
+ u8 trp;
+ u8 trcd;
+ u8 tras;
+ u8 trfc;
+ u8 twr;
u8 cas; /* 3, 4 or 5 */
u8 refresh; /* 0 = 15.6us, 1 = 7.8us */
@@ -50,14 +53,13 @@ struct sys_info {
#define SYSINFO_PACKAGE_PLANAR 0x00
#define SYSINFO_PACKAGE_STACKED 0x01
u8 dimm[2 * DIMM_SOCKETS];
+ dimm_attr dimm_info[2 * DIMM_SOCKETS];
#define SYSINFO_DIMM_X16DS 0x00
#define SYSINFO_DIMM_X8DS 0x01
#define SYSINFO_DIMM_X16SS 0x02
#define SYSINFO_DIMM_X8DDS 0x03
#define SYSINFO_DIMM_NOT_POPULATED 0x04
- u8 banks[2 * DIMM_SOCKETS];
-
u8 banksize[2 * 2 * DIMM_SOCKETS];
const u8 *spd_addresses;
1
0

Patch set updated for coreboot: nb/i945/raminit: Use common ddr2 decode functions
by Arthur Heymans Feb. 28, 2017
by Arthur Heymans Feb. 28, 2017
Feb. 28, 2017
Arthur Heymans (arthur(a)aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18305
-gerrit
commit 26938719c01de55fface6445d5d9e8a6835124bb
Author: Arthur Heymans <arthur(a)aheymans.xyz>
Date: Mon Feb 6 22:40:14 2017 +0100
nb/i945/raminit: Use common ddr2 decode functions
This simplifies computing dram timings a lot.
This removes computation of rank size based on columns, rows,
banks,... and uses the information in SPD byte 31.
The result of this is that dimms with multiple asymmetric ranks are
not supported anymore. These however are very rare and most likely
never tested on this platform.
Change-Id: I97c93939d11807752797785dd88c70b43a236ee3
Signed-off-by: Arthur Heymans <arthur(a)aheymans.xyz>
---
src/northbridge/intel/i945/raminit.c | 614 +++++++----------------------------
src/northbridge/intel/i945/raminit.h | 16 +-
2 files changed, 121 insertions(+), 509 deletions(-)
diff --git a/src/northbridge/intel/i945/raminit.c b/src/northbridge/intel/i945/raminit.c
index 782f4f0..470140e 100644
--- a/src/northbridge/intel/i945/raminit.c
+++ b/src/northbridge/intel/i945/raminit.c
@@ -2,6 +2,7 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2017 Arthur Heymans <arthur(a)aheymans.xyz>
*
* 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
@@ -28,6 +29,7 @@
#include "i945.h"
#include "chip.h"
#include <cbmem.h>
+#include <device/dram/ddr2.h>
/* Debugging macros. */
#if CONFIG_DEBUG_RAM_SETUP
@@ -315,8 +317,6 @@ static void sdram_detect_errors(struct sys_info *sysinfo)
static void sdram_get_dram_configuration(struct sys_info *sysinfo)
{
u32 dimm_mask = 0;
- int i;
-
/**
* i945 supports two DIMMs, in two configurations:
*
@@ -357,9 +357,11 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
*
*/
- for (i = 0; i<(2 * DIMM_SOCKETS); i++) {
+ int i, j, spd_size;
+ spd_raw_data raw_spd;
+
+ for (i = 0; i < (2 * DIMM_SOCKETS); i++) {
int device = get_dimm_spd_address(sysinfo, i);
- u8 reg8;
/* Initialize the socket information with a sane value */
sysinfo->dimm[i] = SYSINFO_DIMM_NOT_POPULATED;
@@ -368,31 +370,40 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
if (!sdram_capabilities_dual_channel() && (i >> 1))
continue;
- printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: ", (i >> 1), (i & 1));
-
if (spd_read_byte(device, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR2) {
- printk(BIOS_DEBUG, "N/A\n");
+ printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: N/A\n", (i >> 1), (i & 1));
continue;
}
- reg8 = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE);
- if (reg8 == ERROR_SCHEME_ECC)
- die("Error: ECC memory not supported by this chipset\n");
+ spd_size = spd_decode_spd_size_ddr2(spd_read_byte(device, 0));
+ if (spd_size > sizeof(raw_spd))
+ continue;
+
+ for (j = 0; j < spd_size; j++)
+ raw_spd[j] = spd_read_byte(device, j);
+
+ if (spd_decode_ddr2(&sysinfo->dimm_info[i], raw_spd)
+ != SPD_STATUS_OK)
+ continue;
+
+ if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP))
+ dram_print_spd_ddr2(&sysinfo->dimm_info[i]);
+
+ if (sysinfo->dimm_info[i].flags.is_ecc)
+ die("\nError: ECC memory not supported by this chipset\n");
- reg8 = spd_read_byte(device, SPD_MODULE_ATTRIBUTES);
- if (reg8 & MODULE_BUFFERED)
- die("Error: Buffered memory not supported by this chipset\n");
- if (reg8 & MODULE_REGISTERED)
- die("Error: Registered memory not supported by this chipset\n");
+ if (spd_dimm_is_registered_ddr2(sysinfo->dimm_info[i].dimm_type))
+ die("\nError: Registered memory not supported by this chipset\n");
- switch (spd_read_byte(device, SPD_PRIMARY_SDRAM_WIDTH)) {
+ printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: ", (i >> 1), (i & 1));
+ switch (sysinfo->dimm_info[i].width) {
case 0x08:
- switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
- case 1:
+ switch (sysinfo->dimm_info[i].ranks) {
+ case 2:
printk(BIOS_DEBUG, "x8DDS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X8DDS;
break;
- case 0:
+ case 1:
printk(BIOS_DEBUG, "x8DS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X8DS;
break;
@@ -401,12 +412,12 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
}
break;
case 0x10:
- switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
- case 1:
+ switch (sysinfo->dimm_info[i].ranks) {
+ case 2:
printk(BIOS_DEBUG, "x16DS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X16DS;
break;
- case 0:
+ case 1:
printk(BIOS_DEBUG, "x16SS\n");
sysinfo->dimm[i] = SYSINFO_DIMM_X16SS;
break;
@@ -417,15 +428,14 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
default:
die("Unsupported DDR-II memory width.\n");
}
-
dimm_mask |= (1 << i);
}
-
if (!dimm_mask) {
die("No memory installed.\n");
}
if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) {
+ /* Possibly does not boot in this case */
printk(BIOS_INFO, "Channel 0 has no memory populated.\n");
}
}
@@ -438,7 +448,6 @@ static void sdram_get_dram_configuration(struct sys_info *sysinfo)
static void sdram_verify_package_type(struct sys_info * sysinfo)
{
int i;
-
/* Assume no stacked DIMMs are available until we find one */
sysinfo->package = 0;
for (i = 0; i < 2*DIMM_SOCKETS; i++) {
@@ -446,365 +455,86 @@ static void sdram_verify_package_type(struct sys_info * sysinfo)
continue;
/* Is the current DIMM a stacked DIMM? */
- if (spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_NUM_DIMM_BANKS) & (1 << 4))
+ if (sysinfo->dimm_info[i].flags.stacked)
sysinfo->package = 1;
}
}
-static u8 sdram_possible_cas_latencies(struct sys_info * sysinfo)
+static void select_cas_dramfreq(struct sys_info * sysinfo)
{
- int i;
- u8 cas_mask;
-
- /* Setup CAS mask with all supported CAS Latencies */
- cas_mask = SPD_CAS_LATENCY_DDR2_3 |
- SPD_CAS_LATENCY_DDR2_4 |
- SPD_CAS_LATENCY_DDR2_5;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- if (sysinfo->dimm[i] != SYSINFO_DIMM_NOT_POPULATED)
- cas_mask &= spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_ACCEPTABLE_CAS_LATENCIES);
- }
-
- if (!cas_mask) {
- die("No DDR-II modules with accepted CAS latencies found.\n");
- }
-
- return cas_mask;
-}
-
-static void sdram_detect_cas_latency_and_ram_speed(struct sys_info * sysinfo, u8 cas_mask)
-{
- int i, j, idx;
- int lowest_common_cas = 0;
- int max_ram_speed = 0;
-
- const u8 ddr2_speeds_table[] = {
- 0x50, 0x60, /* DDR2 400: tCLK = 5.0ns tAC = 0.6ns */
- 0x3d, 0x50, /* DDR2 533: tCLK = 3.75ns tAC = 0.5ns */
- 0x30, 0x45, /* DDR2 667: tCLK = 3.0ns tAC = 0.45ns */
- };
-
- const u8 spd_lookup_table[] = {
- SPD_MIN_CYCLE_TIME_AT_CAS_MAX, SPD_ACCESS_TIME_FROM_CLOCK,
- SPD_SDRAM_CYCLE_TIME_2ND, SPD_ACCESS_TIME_FROM_CLOCK_2ND,
- SPD_SDRAM_CYCLE_TIME_3RD, SPD_ACCESS_TIME_FROM_CLOCK_3RD
- };
+ u8 selected_cas;
+ u8 cas_mask = SPD_CAS_LATENCY_DDR2_3 | SPD_CAS_LATENCY_DDR2_4
+ | SPD_CAS_LATENCY_DDR2_5;
+ u32 common_tCLK, min_tCLK = TCK_200MHZ;
switch (sdram_capabilities_max_supported_memory_frequency()) {
- case 400: max_ram_speed = 0; break;
- case 533: max_ram_speed = 1; break;
- case 667: max_ram_speed = 2; break;
- }
-
- sysinfo->memory_frequency = 0;
- sysinfo->cas = 0;
-
- if (cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- lowest_common_cas = 3;
- } else if (cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- lowest_common_cas = 4;
- } else if (cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- lowest_common_cas = 5;
+ case 400: min_tCLK = TCK_200MHZ; break;
+ case 533: min_tCLK = TCK_266MHZ; break;
+ case 667: min_tCLK = TCK_333MHZ; break;
}
- PRINTK_DEBUG("lowest common cas = %d\n", lowest_common_cas);
- for (j = max_ram_speed; j>=0; j--) {
- int freq_cas_mask = cas_mask;
-
- PRINTK_DEBUG("Probing Speed %d\n", j);
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- int device = get_dimm_spd_address(sysinfo, i);
- int current_cas_mask;
-
- PRINTK_DEBUG(" DIMM: %d\n", i);
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
- continue;
- }
-
- current_cas_mask = spd_read_byte(device, SPD_ACCEPTABLE_CAS_LATENCIES);
-
- while (current_cas_mask) {
- int highest_supported_cas = 0, current_cas = 0;
- PRINTK_DEBUG(" Current CAS mask: %04x; ", current_cas_mask);
- if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- highest_supported_cas = 5;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- highest_supported_cas = 4;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- highest_supported_cas = 3;
- } else {
- die("Invalid max. CAS.\n");
- }
- if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- current_cas = 3;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- current_cas = 4;
- } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- current_cas = 5;
- } else {
- die("Invalid CAS.\n");
- }
-
- idx = highest_supported_cas - current_cas;
- PRINTK_DEBUG("idx=%d, ", idx);
- PRINTK_DEBUG("tCLK=%x, ", spd_read_byte(device, spd_lookup_table[2*idx]));
- PRINTK_DEBUG("tAC=%x", spd_read_byte(device, spd_lookup_table[(2*idx)+1]));
-
- if (spd_read_byte(device, spd_lookup_table[2*idx]) <= ddr2_speeds_table[2*j] &&
- spd_read_byte(device, spd_lookup_table[(2*idx)+1]) <= ddr2_speeds_table[(2*j)+1]) {
- PRINTK_DEBUG(": OK\n");
- break;
- }
-
- PRINTK_DEBUG(": Not fast enough!\n");
-
- current_cas_mask &= ~(1 << (current_cas));
- }
-
- freq_cas_mask &= current_cas_mask;
- if (!current_cas_mask) {
- PRINTK_DEBUG(" No valid CAS for this speed on DIMM %d\n", i);
- break;
- }
- }
- PRINTK_DEBUG(" freq_cas_mask for speed %d: %04x\n", j, freq_cas_mask);
- if (freq_cas_mask) {
- switch (j) {
- case 0: sysinfo->memory_frequency = 400; break;
- case 1: sysinfo->memory_frequency = 533; break;
- case 2: sysinfo->memory_frequency = 667; break;
- }
- if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
- sysinfo->cas = 3;
- } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
- sysinfo->cas = 4;
- } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
- sysinfo->cas = 5;
- }
- break;
- }
- }
-
- if (sysinfo->memory_frequency && sysinfo->cas) {
- printk(BIOS_DEBUG, "Memory will be driven at %dMHz with CAS=%d clocks\n",
- sysinfo->memory_frequency, sysinfo->cas);
- } else {
+ common_tCLK = get_common_freq_cas(cas_mask, sysinfo->dimm_info,
+ 2 * DIMM_SOCKETS, &selected_cas);
+ if (common_tCLK == 0)
die("Could not find common memory frequency and CAS\n");
- }
-}
-
-static void sdram_detect_smallest_tRAS(struct sys_info * sysinfo)
-{
- int i;
- int tRAS_time;
- int tRAS_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRAS_cycles = 4; /* 4 clocks minimum */
- tRAS_time = tRAS_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY);
- if (!reg8) {
- die("Invalid tRAS value.\n");
- }
-
- while ((tRAS_time >> 2) < reg8) {
- tRAS_time += freq_multiplier;
- tRAS_cycles++;
- }
- }
- if (tRAS_cycles > 0x18) {
- die("DDR-II Module does not support this frequency (tRAS error)\n");
- }
-
- printk(BIOS_DEBUG, "tRAS = %d cycles\n", tRAS_cycles);
- sysinfo->tras = tRAS_cycles;
-}
-
-static void sdram_detect_smallest_tRP(struct sys_info * sysinfo)
-{
- int i;
- int tRP_time;
- int tRP_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRP_cycles = 2; /* 2 clocks minimum */
- tRP_time = tRP_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
+ common_tCLK = MAX(common_tCLK, min_tCLK);
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ROW_PRECHARGE_TIME);
- if (!reg8) {
- die("Invalid tRP value.\n");
- }
-
- while (tRP_time < reg8) {
- tRP_time += freq_multiplier;
- tRP_cycles++;
- }
- }
+ sysinfo->cas = selected_cas;
+ sysinfo->tclk = common_tCLK;
- if (tRP_cycles > 6) {
- die("DDR-II Module does not support this frequency (tRP error)\n");
+ switch (sysinfo->tclk) {
+ case TCK_200MHZ: sysinfo->memory_frequency = 400; break;
+ case TCK_266MHZ: sysinfo->memory_frequency = 533; break;
+ case TCK_333MHZ: sysinfo->memory_frequency = 667; break;
}
-
- printk(BIOS_DEBUG, "tRP = %d cycles\n", tRP_cycles);
- sysinfo->trp = tRP_cycles;
+ printk(BIOS_DEBUG, "Memory will be driven at %dMHz with CAS=%d clocks\n",
+ sysinfo->memory_frequency, sysinfo->cas);
}
-static void sdram_detect_smallest_tRCD(struct sys_info * sysinfo)
+static void select_dram_timings(struct sys_info * sysinfo)
{
int i;
- int tRCD_time;
- int tRCD_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tRCD_cycles = 2; /* 2 clocks minimum */
- tRCD_time = tRCD_cycles * freq_multiplier;
+ int tCLK = sysinfo->tclk;
+ int tRAS_cycles, tRP_cycles, tRCD_cycles, tWR_cycles, tRFC_cycles;
for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_RAS_TO_CAS_DELAY);
- if (!reg8) {
- die("Invalid tRCD value.\n");
- }
-
- while (tRCD_time < reg8) {
- tRCD_time += freq_multiplier;
- tRCD_cycles++;
- }
- }
- if (tRCD_cycles > 6) {
- die("DDR-II Module does not support this frequency (tRCD error)\n");
- }
-
- printk(BIOS_DEBUG, "tRCD = %d cycles\n", tRCD_cycles);
- sysinfo->trcd = tRCD_cycles;
-}
-
-static void sdram_detect_smallest_tWR(struct sys_info * sysinfo)
-{
- int i;
- int tWR_time;
- int tWR_cycles;
- int freq_multiplier = 0;
-
- switch (sysinfo->memory_frequency) {
- case 400: freq_multiplier = 0x14; break; /* 5ns */
- case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
- case 667: freq_multiplier = 0x0c; break; /* 3ns */
- }
-
- tWR_cycles = 2; /* 2 clocks minimum */
- tWR_time = tWR_cycles * freq_multiplier;
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_WRITE_RECOVERY_TIME);
- if (!reg8) {
- die("Invalid tWR value.\n");
- }
-
- while (tWR_time < reg8) {
- tWR_time += freq_multiplier;
- tWR_cycles++;
- }
- }
- if (tWR_cycles > 5) {
- die("DDR-II Module does not support this frequency (tWR error)\n");
- }
-
- printk(BIOS_DEBUG, "tWR = %d cycles\n", tWR_cycles);
- sysinfo->twr = tWR_cycles;
-}
-
-static void sdram_detect_smallest_tRFC(struct sys_info * sysinfo)
-{
- int i, index = 0;
-
- const u8 tRFC_cycles[] = {
- /* 75 105 127.5 */
- 15, 21, 26, /* DDR2-400 */
- 20, 28, 34, /* DDR2-533 */
- 25, 35, 43 /* DDR2-667 */
- };
-
- for (i = 0; i < 2*DIMM_SOCKETS; i++) {
- u8 reg8;
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
- continue;
-
- reg8 = sysinfo->banksize[i*2];
- switch (reg8) {
- case 0x04: reg8 = 0; break;
- case 0x08: reg8 = 1; break;
- case 0x10: reg8 = 2; break;
- case 0x20: reg8 = 3; break;
- }
-
- if (sysinfo->dimm[i] == SYSINFO_DIMM_X16DS || sysinfo->dimm[i] == SYSINFO_DIMM_X16SS)
- reg8++;
-
- if (reg8 > 3) {
- /* Can this happen? Go back to 127.5ns just to be sure
- * we don't run out of the array. This may be wrong
- */
- printk(BIOS_DEBUG, "DIMM %d is 1Gb x16.. Please report.\n", i);
- reg8 = 3;
- }
-
- if (reg8 > index)
- index = reg8;
-
- }
- index--;
- switch (sysinfo->memory_frequency) {
- case 667: index += 3; /* Fallthrough */
- case 533: index += 3; /* Fallthrough */
- case 400: break;
- }
-
- sysinfo->trfc = tRFC_cycles[index];
- printk(BIOS_DEBUG, "tRFC = %d cycles\n", tRFC_cycles[index]);
+ tRAS_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRAS, tCLK);
+ if (tRAS_cycles > 0x18)
+ die("DDR-II Module does not support this frequency (tRAS error)\n");
+ if (tRAS_cycles > sysinfo->tras)
+ sysinfo->tras = tRAS_cycles;
+
+ tRP_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRP, tCLK);
+ if (tRP_cycles > 6)
+ die("DDR-II Module does not support this frequency (tRP error)\n");
+ if (tRP_cycles > sysinfo->trp)
+ sysinfo->trp = tRP_cycles;
+
+ tRCD_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRCD, tCLK);
+ if (tRCD_cycles > 6)
+ die("DDR-II Module does not support this frequency (tRCD error)\n");
+ if (tRCD_cycles > sysinfo->trcd)
+ sysinfo->trcd = tRCD_cycles;
+
+ tWR_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tWR, tCLK);
+ if (tWR_cycles > 5)
+ die("DDR-II Module does not support this frequency (tWR error)\n");
+ if (tWR_cycles > sysinfo->twr)
+ sysinfo->twr = tWR_cycles;
+
+ tRFC_cycles = DIV_ROUND_UP(sysinfo->dimm_info[i].tRFC, tCLK);
+ if (tRFC_cycles > sysinfo->trfc)
+ sysinfo->trfc = tRFC_cycles;
+
+ }
+ printk(BIOS_DEBUG, "tRAS = %d cycles\n", sysinfo->tras);
+ printk(BIOS_DEBUG, "tRP = %d cycles\n", sysinfo->trp);
+ printk(BIOS_DEBUG, "tRCD = %d cycles\n", sysinfo->trcd);
+ printk(BIOS_DEBUG, "tWR = %d cycles\n", sysinfo->twr);
+ printk(BIOS_DEBUG, "tRFC = %d cycles\n", sysinfo->trfc);
}
static void sdram_detect_smallest_refresh(struct sys_info * sysinfo)
@@ -819,23 +549,20 @@ static void sdram_detect_smallest_refresh(struct sys_info * sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- refresh = spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_REFRESH) & ~(1 << 7);
-
- /* 15.6us */
- if (!refresh)
- continue;
+ refresh = sysinfo->dimm_info[i].tRR;
/* Refresh is slower than 15.6us, use 15.6us */
- if (refresh > 2)
+ /* tRR is decoded in units of 1/256us */
+ if (refresh >= 15625 * 256)
continue;
- if (refresh == 2) {
+ if (refresh == 15625 * 256 / 2) {
sysinfo->refresh = 1;
break;
}
- die("DDR-II module has unsupported refresh value\n");
+ if (refresh == 15625 * 256 / 4)
+ die("DDR-II module has unsupported refresh value\n");
}
printk(BIOS_DEBUG, "Refresh: %s\n", sysinfo->refresh?"7.8us":"15.6us");
}
@@ -848,8 +575,7 @@ static void sdram_verify_burst_length(struct sys_info * sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- if (!(spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_SUPPORTED_BURST_LENGTHS) & SPD_BURST_LENGTH_8))
+ if (!sysinfo->dimm_info[i].flags.bl8)
die("Only DDR-II RAM with burst length 8 is supported by this chipset.\n");
}
}
@@ -1376,121 +1102,26 @@ static void sdram_enable_system_memory_io(struct sys_info *sysinfo)
}
}
-struct dimm_size {
- unsigned long side1;
- unsigned long side2;
-};
-
-static struct dimm_size sdram_get_dimm_size(struct sys_info *sysinfo, u16 dimmno)
-{
- /* Calculate the log base 2 size of a DIMM in bits */
- struct dimm_size sz;
- int value, low, rows, columns, device;
-
- device = get_dimm_spd_address(sysinfo, dimmno);
- sz.side1 = 0;
- sz.side2 = 0;
-
- rows = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
- if (rows < 0) goto hw_err;
- if ((rows & 0xf) == 0) goto val_err;
- sz.side1 += rows & 0xf;
-
- columns = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
- if (columns < 0) goto hw_err;
- if ((columns & 0xf) == 0) goto val_err;
- sz.side1 += columns & 0xf;
-
- value = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM); /* banks */
- if (value < 0) goto hw_err;
- if ((value & 0xff) == 0) goto val_err;
- sz.side1 += log2(value & 0xff);
-
- /* Get the module data width and convert it to a power of two */
- value = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_MSB); /* (high byte) */
- if (value < 0) goto hw_err;
- value &= 0xff;
- value <<= 8;
-
- low = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB); /* (low byte) */
- if (low < 0) goto hw_err;
- value = value | (low & 0xff);
- if ((value != 72) && (value != 64)) goto val_err;
- sz.side1 += log2(value);
-
- /* side 2 */
- value = spd_read_byte(device, SPD_NUM_DIMM_BANKS); /* number of physical banks */
-
- if (value < 0) goto hw_err;
- value &= 7;
- value++;
- if (value == 1) goto out;
- if (value != 2) goto val_err;
-
- /* Start with the symmetrical case */
- sz.side2 = sz.side1;
-
- if ((rows & 0xf0) == 0) goto out; /* If symmetrical we are done */
-
- /* Don't die here, I have not come across any of these to test what
- * actually happens.
- */
- printk(BIOS_ERR, "Asymmetric DIMMs are not supported by this chipset\n");
-
- sz.side2 -= (rows & 0x0f); /* Subtract out rows on side 1 */
- sz.side2 += ((rows >> 4) & 0x0f); /* Add in rows on side 2 */
-
- sz.side2 -= (columns & 0x0f); /* Subtract out columns on side 1 */
- sz.side2 += ((columns >> 4) & 0x0f); /* Add in columns on side 2 */
-
- goto out;
-
- val_err:
- die("Bad SPD value\n");
- hw_err:
- /* If a hardware error occurs the spd ROM probably does not exist.
- * In this case report that there is no memory
- */
- sz.side1 = 0;
- sz.side2 = 0;
-out:
- return sz;
-}
-
static void sdram_detect_dimm_size(struct sys_info * sysinfo)
{
int i;
for (i = 0; i < 2 * DIMM_SOCKETS; i++) {
- struct dimm_size sz;
-
sysinfo->banksize[i * 2] = 0;
sysinfo->banksize[(i * 2) + 1] = 0;
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
-
- sz = sdram_get_dimm_size(sysinfo, i);
-
- sysinfo->banks[i] = spd_read_byte(get_dimm_spd_address(sysinfo, i),
- SPD_NUM_BANKS_PER_SDRAM); /* banks */
-
- if (sz.side1 < 30)
+ if (sysinfo->dimm_info[i].ranksize_mb < 128)
die("DDR-II rank size smaller than 128MB is not supported.\n");
- sysinfo->banksize[i * 2] = 1 << (sz.side1 - 28);
-
+ sysinfo->banksize[i * 2] = sysinfo->dimm_info[i].ranksize_mb / 32;
printk(BIOS_DEBUG, "DIMM %d side 0 = %d MB\n", i, sysinfo->banksize[i * 2] * 32 );
- if (!sz.side2)
+ if (sysinfo->dimm_info[i].ranks == 1)
continue;
- /* If there is a second side, it has to have at least 128M, too */
- if (sz.side2 < 30)
- die("DDR-II rank size smaller than 128MB is not supported.\n");
-
- sysinfo->banksize[(i * 2) + 1] = 1 << (sz.side2 - 28);
-
+ sysinfo->banksize[(i * 2) + 1] = sysinfo->dimm_info[i].ranksize_mb / 32;
printk(BIOS_DEBUG, "DIMM %d side 1 = %d MB\n", i, sysinfo->banksize[(i * 2) + 1] * 32);
}
}
@@ -1564,25 +1195,19 @@ static int sdram_program_row_boundaries(struct sys_info *sysinfo)
static int sdram_set_row_attributes(struct sys_info *sysinfo)
{
- int i, value;
+ int i;
u16 dra0 = 0, dra1 = 0, dra = 0;
printk(BIOS_DEBUG, "Setting row attributes...\n");
for (i = 0; i < 2 * DIMM_SOCKETS; i++) {
- u16 device;
u8 columnsrows;
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
continue;
}
- device = get_dimm_spd_address(sysinfo, i);
-
- value = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
- columnsrows = (value & 0x0f);
-
- value = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
- columnsrows |= (value & 0xf) << 4;
+ columnsrows = (sysinfo->dimm_info[i].row_bits & 0x0f)
+ | (sysinfo->dimm_info[i].col_bits & 0xf) << 4;
switch (columnsrows) {
case 0x9d: dra = 2; break;
@@ -1630,7 +1255,7 @@ static void sdram_set_bank_architecture(struct sys_info *sysinfo)
if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
continue;
- if (sysinfo->banks[i] != 8)
+ if (sysinfo->dimm_info[i].banks != 8)
continue;
printk(BIOS_SPEW, "DIMM%d has 8 banks.\n", i);
@@ -3039,7 +2664,7 @@ static void sdram_setup_processor_side(void)
void sdram_initialize(int boot_path, const u8 *spd_addresses)
{
struct sys_info sysinfo;
- u8 reg8, cas_mask;
+ u8 reg8;
printk(BIOS_DEBUG, "Setting up RAM controller.\n");
@@ -3057,20 +2682,11 @@ void sdram_initialize(int boot_path, const u8 *spd_addresses)
/* Check whether we have stacked DIMMs */
sdram_verify_package_type(&sysinfo);
- /* Determine common CAS */
- cas_mask = sdram_possible_cas_latencies(&sysinfo);
+ /* Select common CAS latency and dram frequency */
+ select_cas_dramfreq(&sysinfo);
- /* Choose Common Frequency */
- sdram_detect_cas_latency_and_ram_speed(&sysinfo, cas_mask);
-
- /* Determine smallest common tRAS */
- sdram_detect_smallest_tRAS(&sysinfo);
-
- /* Determine tRP */
- sdram_detect_smallest_tRP(&sysinfo);
-
- /* Determine tRCD */
- sdram_detect_smallest_tRCD(&sysinfo);
+ /* Select nRAS, nRP, nRCD, nWR, tRFC */
+ select_dram_timings(&sysinfo);
/* Determine smallest refresh period */
sdram_detect_smallest_refresh(&sysinfo);
@@ -3078,15 +2694,9 @@ void sdram_initialize(int boot_path, const u8 *spd_addresses)
/* Verify all DIMMs support burst length 8 */
sdram_verify_burst_length(&sysinfo);
- /* determine tWR */
- sdram_detect_smallest_tWR(&sysinfo);
-
/* Determine DIMM size parameters (rows, columns banks) */
sdram_detect_dimm_size(&sysinfo);
- /* determine tRFC */
- sdram_detect_smallest_tRFC(&sysinfo);
-
/* Program PLL settings */
sdram_program_pll_settings(&sysinfo);
diff --git a/src/northbridge/intel/i945/raminit.h b/src/northbridge/intel/i945/raminit.h
index 0554900..965998a 100644
--- a/src/northbridge/intel/i945/raminit.h
+++ b/src/northbridge/intel/i945/raminit.h
@@ -23,15 +23,18 @@
/* Burst length is always 8 */
#define BURSTLENGTH 8
+#include <device/dram/ddr2.h>
+
struct sys_info {
u16 memory_frequency; /* 400, 533 or 667 */
u16 fsb_frequency; /* 945GM: 400, 533 or 667 / 945GC: 533, 800, or 1066 */
+ u32 tclk;
- u8 trp; /* calculated by sdram_detect_smallest_tRP() */
- u8 trcd; /* calculated by sdram_detect_smallest_tRCD() */
- u8 tras; /* calculated by sdram_detect_smallest_tRAS() */
- u8 trfc; /* calculated by sdram_detect_smallest_tRFC() */
- u8 twr; /* calculated by sdram_detect_smallest_tWR() */
+ u8 trp;
+ u8 trcd;
+ u8 tras;
+ u8 trfc;
+ u8 twr;
u8 cas; /* 3, 4 or 5 */
u8 refresh; /* 0 = 15.6us, 1 = 7.8us */
@@ -50,14 +53,13 @@ struct sys_info {
#define SYSINFO_PACKAGE_PLANAR 0x00
#define SYSINFO_PACKAGE_STACKED 0x01
u8 dimm[2 * DIMM_SOCKETS];
+ dimm_attr dimm_info[2 * DIMM_SOCKETS];
#define SYSINFO_DIMM_X16DS 0x00
#define SYSINFO_DIMM_X8DS 0x01
#define SYSINFO_DIMM_X16SS 0x02
#define SYSINFO_DIMM_X8DDS 0x03
#define SYSINFO_DIMM_NOT_POPULATED 0x04
- u8 banks[2 * DIMM_SOCKETS];
-
u8 banksize[2 * 2 * DIMM_SOCKETS];
const u8 *spd_addresses;
1
0

Patch set updated for coreboot: nb/intel/i945/raminit.c: Refactor tRD selection
by Arthur Heymans Feb. 28, 2017
by Arthur Heymans Feb. 28, 2017
Feb. 28, 2017
Arthur Heymans (arthur(a)aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18354
-gerrit
commit 2ab2564611415ddc7bec9091eca0deee75c0fee9
Author: Arthur Heymans <arthur(a)aheymans.xyz>
Date: Sun Feb 12 23:34:39 2017 +0100
nb/intel/i945/raminit.c: Refactor tRD selection
Inspired by gm45 code, which sets this value the same way.
Some values for tRD on 800 and 1067MHz FSB were set wrong because the
CAS/Freq selection was wrong.
PASSED memtest86+ during 10h+ on 1067MHZ fsb with 667MHz ddr2, CAS 5
Change-Id: I8002daf25b7603131b78b01075f43fd23747dd94
Signed-off-by: Arthur Heymans <arthur(a)aheymans.xyz>
---
src/northbridge/intel/i945/raminit.c | 49 ++++++++++--------------------------
1 file changed, 13 insertions(+), 36 deletions(-)
diff --git a/src/northbridge/intel/i945/raminit.c b/src/northbridge/intel/i945/raminit.c
index cc227cc..782f4f0 100644
--- a/src/northbridge/intel/i945/raminit.c
+++ b/src/northbridge/intel/i945/raminit.c
@@ -1720,21 +1720,11 @@ static void sdram_program_odt_tristate(struct sys_info *sysinfo)
static void sdram_set_timing_and_control(struct sys_info *sysinfo)
{
- u32 reg32, off32;
+ u32 reg32, tRD_min;
u32 tWTR;
u32 temp_drt;
int i, page_size;
- static const u8 drt0_table[] = {
- /* CL 3, 4, 5 */
- 3, 4, 5, /* FSB533, DDR667/533/400 */
- 4, 5, 6, /* FSB667, DDR667/533/400 */
- 5, 6, 7, /* FSB800, DDR400/533 */
- 6, 7, 8, /* FSB800, DDR667 */
- 5, 6, 7, /* FSB1066, DDR400 */
- 7, 8, 9, /* FSB1066, DDR533/DDR667 */
- };
-
static const u8 cas_table[] = {
2, 1, 0, 3
};
@@ -1787,34 +1777,21 @@ static void sdram_set_timing_and_control(struct sys_info *sysinfo)
/* CxDRT0 [23:22], [21:20], [19:18] [16] have fixed values */
temp_drt |= ( (1 << 22) | (3 << 20) | (1 << 18) | (0 << 16) );
- /* Program Write Auto Precharge to Activate */
- off32 = 0;
+ /*
+ * tRD is the delay the memory controller is waiting on the FSB,
+ * in mclk domain.
+ * This parameter is important for stability and performance.
+ * Those values might not be optimal but seem stable.
+ */
+ tRD_min = sysinfo->cas;
switch (sysinfo->fsb_frequency) {
- case 533:
- off32 = 0;
- break;
- case 667:
- off32 = 3;
- break;
- case 800:
- if (sysinfo->memory_frequency <= 533) {
- off32 = 6;
- break;
- }
- off32 = 9;
- break;
- case 1066:
- if (sysinfo->memory_frequency == 400) {
- off32 = 12;
- break;
- }
- off32 = 15;
- break;
+ case 533: break;
+ case 667: tRD_min += 1; break;
+ case 800: tRD_min += 2; break;
+ case 1066: tRD_min += 3; break;
}
- off32 += sysinfo->cas - 3;
- reg32 = drt0_table[off32];
- temp_drt |= (reg32 << 11);
+ temp_drt |= (tRD_min << 11);
/* Read Auto Precharge to Activate */
1
0

Feb. 28, 2017
the following patch was just integrated into master:
commit 068edc1c52cb1e5b6376ba7f296ef8797a24cd5f
Author: Nicola Corna <nicola(a)corna.info>
Date: Sat Feb 11 14:52:24 2017 +0100
ec/lenovo/h8: Fix mute LEDs
thinkpad_acpi expects a SSMS method to turn on/off the mute LED
and a MMTS method to turn on/off the microphone mute LED. With
these methods implemented the driver can correctly sync the LEDs
with the corresponding statuses.
There seems to be two different bits to mute the audio in the
Lenovo H8 EC:
* AMUT, used internally (for example to disable the audio before
entering S3).
* ALMT, controllable by the OS, which also toggles the mute LED
(if present).
Tested on a X220T and on a X201.
Change-Id: I578f95f9619a53fd35f8a8bfe5564aeb6c789212
Signed-off-by: Nicola Corna <nicola(a)corna.info>
Reviewed-on: https://review.coreboot.org/18329
Reviewed-by: Alexander Couzens <lynxis(a)fe80.eu>
Tested-by: build bot (Jenkins)
See https://review.coreboot.org/18329 for details.
-gerrit
1
0