aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineeringinc.com>2015-03-27 22:58:45 -0500
committerAaron Durbin <adurbin@gmail.com>2015-04-02 05:55:41 +0200
commit6e523a682f0284ec1cb810c6b4b4fb61702d651a (patch)
tree774bf1abd6ca8d90c7e61945c86773a3dbcb8a9c /src/northbridge
parenta73dcbe736fb565e932bccee6a34c4c566e89431 (diff)
northbridge/amd/amdfam10: Generate SMBIOS tables for RAM
TEST: Booted ASUS KFSN4-DRE and verified SMBIOS contents via dmidecode utility. Change-Id: Id656f2f6cf5a4ecafa03e150ad91f69107a4fe88 Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: http://review.coreboot.org/9140 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@gmail.com>
Diffstat (limited to 'src/northbridge')
-rw-r--r--src/northbridge/amd/amdfam10/northbridge.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
index 9206de7e3a..f857b7289a 100644
--- a/src/northbridge/amd/amdfam10/northbridge.c
+++ b/src/northbridge/amd/amdfam10/northbridge.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <lib.h>
+#include <smbios.h>
#include <cpu/cpu.h>
#include <cpu/x86/lapic.h>
@@ -43,6 +44,7 @@
#include "northbridge.h"
#include "amdfam10.h"
#include "ht_config.h"
+#include "chip.h"
#if CONFIG_HW_MEM_HOLE_SIZEK != 0
#include <cpu/amd/model_10xxx_rev.h>
@@ -945,6 +947,178 @@ static u32 amdfam10_domain_scan_bus(device_t dev, u32 max)
return max;
}
+#if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLES)
+static int amdfam10_get_smbios_data16(int* count, int handle, unsigned long *current)
+{
+ struct amdmct_memory_info *mem_info;
+ mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO);
+ if (mem_info == NULL)
+ return 0; /* can't find amdmct information in cbmem */
+
+ struct device *dev = get_node_pci(0, 0);
+ struct northbridge_amd_amdfam10_config *config = dev->chip_info;
+
+ int node;
+ int slot;
+
+ struct smbios_type16 *t = (struct smbios_type16 *)*current;
+ int len = sizeof(struct smbios_type16);
+
+ memset(t, 0, sizeof(struct smbios_type16));
+ t->type = SMBIOS_PHYS_MEMORY_ARRAY;
+ t->handle = handle;
+ t->length = len - 2;
+ t->location = MEMORY_ARRAY_LOCATION_SYSTEM_BOARD;
+ t->use = MEMORY_ARRAY_USE_SYSTEM;
+ t->memory_error_correction = MEMORY_ARRAY_ECC_NONE;
+ if ((mem_info->ecc_enabled)
+ && (mem_info->mct_stat.GStatus & (1 << GSB_ECCDIMMs))
+ && !(mem_info->mct_stat.GStatus & (1 << GSB_DramECCDis)))
+ /* Single-bit ECC enabled */
+ t->memory_error_correction = MEMORY_ARRAY_ECC_SINGLE_BIT;
+ t->maximum_capacity = config->maximum_memory_capacity / 1024; /* Convert to kilobytes */
+ t->memory_error_information_handle = 0xFFFE; /* no error information handle available */
+
+ t->number_of_memory_devices = 0;
+ /* Check all nodes for installed DIMMs */
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++)
+ /* Check all slots for installed DIMMs */
+ for (slot = 0; slot < MAX_DIMMS_SUPPORTED; slot++)
+ if (mem_info->dct_stat[node].DIMMPresent & (1 << slot))
+ /* Found an installed DIMM; increment count */
+ t->number_of_memory_devices++;
+
+ *current += len;
+ *count += 1;
+ return len;
+}
+
+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;
+ }
+}
+
+static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle, unsigned long *current)
+{
+ struct amdmct_memory_info *mem_info;
+ mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO);
+ if (mem_info == NULL)
+ return 0; /* can't find amdmct information in cbmem */
+
+ int single_len;
+ int len = 0;
+ int node;
+ int slot;
+
+ /* Check all nodes for installed DIMMs */
+ for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+ /* Get configured RAM bus speed */
+ uint16_t speed;
+ speed = amdmct_mct_speed_enum_to_mhz(mem_info->dct_stat[node].Speed);
+
+ /* Get maximum RAM bus speed */
+ uint16_t max_speed;
+ max_speed = amdmct_mct_speed_enum_to_mhz(mem_info->dct_stat[node].DIMMAutoSpeed);
+
+ /* Check all slots for installed DIMMs */
+ for (slot = 0; slot < MAX_DIMMS_SUPPORTED; slot++) {
+ if (mem_info->dct_stat[node].DIMMPresent & (1 << slot)) {
+ /* Found an installed DIMM; populate tables */
+ struct smbios_type17 *t = (struct smbios_type17 *)*current;
+ char string_buffer[256];
+
+ /* Initialize structure */
+ memset(t, 0, sizeof(struct smbios_type17));
+
+ /* Calculate the total module size in bytes:
+ * Primary data width * 2^(#rows) * 2^(#cols) * #banks * #ranks
+ */
+ uint8_t width, rows, cols, banks, ranks;
+ width = 8;
+ rows = mem_info->dct_stat[node].DimmRows[slot];
+ cols = mem_info->dct_stat[node].DimmCols[slot];
+ ranks = mem_info->dct_stat[node].DimmRanks[slot];
+ banks = mem_info->dct_stat[node].DimmBanks[slot];
+ uint64_t dimm_size_bytes = width * (1ULL << rows) * (1ULL << cols) * banks * ranks;
+
+ memset(t, 0, sizeof(struct smbios_type17));
+ t->type = SMBIOS_MEMORY_DEVICE;
+ t->handle = handle;
+ t->phys_memory_array_handle = parent_handle;
+ t->length = sizeof(struct smbios_type17) - 2;
+ if (dimm_size_bytes > 0x800000000) {
+ t->size = 0x7FFF;
+ t->extended_size = dimm_size_bytes;
+ }
+ else {
+ t->size = dimm_size_bytes / (1024*1024);
+ t->size &= (~0x8000); /* size specified in megabytes */
+ }
+ t->total_width = t->data_width = 64;
+ if (mem_info->dct_stat[node].DimmECCPresent & (1 << slot))
+ t->total_width += 8;
+ 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);
+ t->device_locator = smbios_add_string(t->eos, string_buffer);
+ if (IS_ENABLED(CONFIG_DIMM_DDR2))
+ t->memory_type = MEMORY_TYPE_DDR2;
+ else if (IS_ENABLED(CONFIG_DIMM_DDR3))
+ t->memory_type = MEMORY_TYPE_DDR3;
+ t->type_detail = MEMORY_TYPE_DETAIL_SYNCHRONOUS;
+ if (mem_info->dct_stat[node].DimmRegistered[slot])
+ t->type_detail |= MEMORY_TYPE_DETAIL_REGISTERED;
+ else
+ t->type_detail |= MEMORY_TYPE_DETAIL_UNBUFFERED;
+ t->speed = max_speed;
+ t->clock_speed = speed;
+ smbios_fill_dimm_manufacturer_from_id(mem_info->dct_stat[node].DimmManufacturerID[slot], t);
+ 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 {
+ snprintf(string_buffer, sizeof (string_buffer), "%08X", mem_info->dct_stat[node].DimmSerialNumber[slot]);
+ t->serial_number = smbios_add_string(t->eos, string_buffer);
+ }
+ t->memory_error_information_handle = 0xFFFE; /* no error information handle available */
+ single_len = t->length + smbios_string_table_len(t->eos);
+ len += single_len;
+ *current += single_len;
+ handle++;
+ *count += 1;
+ }
+ }
+ }
+
+ return len;
+}
+
+static int amdfam10_get_smbios_data(device_t dev, int *handle, unsigned long *current)
+{
+ int len;
+ int count = 0;
+ len = amdfam10_get_smbios_data16(&count, *handle, current);
+ len += amdfam10_get_smbios_data17(&count, *handle + 1, *handle, current);
+ *handle += count;
+ return len;
+}
+#endif
+
static struct device_operations pci_domain_ops = {
.read_resources = amdfam10_domain_read_resources,
.set_resources = amdfam10_domain_set_resources,
@@ -952,6 +1126,9 @@ static struct device_operations pci_domain_ops = {
.init = NULL,
.scan_bus = amdfam10_domain_scan_bus,
.ops_pci_bus = pci_bus_default_ops,
+#if CONFIG_GENERATE_SMBIOS_TABLES
+ .get_smbios_data = amdfam10_get_smbios_data,
+#endif
};
static void sysconf_init(device_t dev) // first node