From 6e523a682f0284ec1cb810c6b4b4fb61702d651a Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Fri, 27 Mar 2015 22:58:45 -0500 Subject: [PATCH] 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 Reviewed-on: http://review.coreboot.org/9140 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin --- src/northbridge/amd/amdfam10/northbridge.c | 177 +++++++++++++++++++++ 1 file changed, 177 insertions(+) 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 #include #include +#include #include #include @@ -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 @@ -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