1 /* SPDX-License-Identifier: GPL-2.0-or-later */
4 * This code was adapted from src/soc/amd/common/block/pi/amd_late_init.c
8 #include <memory_info.h>
9 #include <console/console.h>
12 #include <ec/google/chromeec/ec.h>
13 #include <bootstate.h>
15 #include <dimm_info_util.h>
17 #include <device/dram/ddr4.h>
18 #include <device/dram/lpddr4.h>
19 #include <device/dram/ddr5.h>
22 * Convert DDR clock speed (based on memory type) in MHz to the standard reported speed in MT/s
24 static uint16_t ddr_speed_mhz_to_reported_mts(uint16_t ddr_type
, uint16_t speed
)
26 if (CONFIG(USE_DDR4
) && ddr_type
== MEMORY_TYPE_DDR4
)
27 return ddr4_speed_mhz_to_reported_mts(speed
);
28 else if (CONFIG(USE_LPDDR4
) && ddr_type
== MEMORY_TYPE_LPDDR4
)
29 return lpddr4_speed_mhz_to_reported_mts(speed
);
30 else if (CONFIG(USE_DDR5
) && (ddr_type
== MEMORY_TYPE_DDR5
||
31 ddr_type
== MEMORY_TYPE_LPDDR5
))
32 return ddr5_speed_mhz_to_reported_mts(speed
);
34 printk(BIOS_ERR
, "Unknown memory type %x\n", ddr_type
);
39 * Return DDR voltage (in mV) based on memory type
41 static uint16_t ddr_get_voltage(uint16_t ddr_type
)
44 case MEMORY_TYPE_DDR4
:
46 case MEMORY_TYPE_LPDDR4
:
47 case MEMORY_TYPE_DDR5
:
49 case MEMORY_TYPE_LPDDR5
:
52 printk(BIOS_ERR
, "Unknown memory type %x\n", ddr_type
);
58 * Populate dimm_info using AGESA TYPE17_DMI_INFO.
60 static void transfer_memory_info(const TYPE17_DMI_INFO
*dmi17
,
61 struct dimm_info
*dimm
)
63 hexstrtobin(dmi17
->SerialNumber
, dimm
->serial
, sizeof(dimm
->serial
));
65 dimm
->dimm_size
= smbios_memory_size_to_mib(dmi17
->MemorySize
, dmi17
->ExtSize
);
67 dimm
->ddr_type
= dmi17
->MemoryType
;
69 dimm
->configured_speed_mts
= ddr_speed_mhz_to_reported_mts(
70 dmi17
->MemoryType
, dmi17
->ConfigSpeed
);
72 dimm
->max_speed_mts
= ddr_speed_mhz_to_reported_mts(dmi17
->MemoryType
, dmi17
->Speed
);
74 dimm
->rank_per_dimm
= dmi17
->Attributes
;
76 dimm
->mod_type
= smbios_form_factor_to_spd_mod_type(
77 (smbios_memory_type
)dmi17
->MemoryType
,
78 (smbios_memory_form_factor
)dmi17
->FormFactor
);
80 dimm
->bus_width
= smbios_bus_width_to_spd_width(dmi17
->MemoryType
, dmi17
->TotalWidth
,
83 dimm
->mod_id
= dmi17
->ManufacturerIdCode
;
85 dimm
->bank_locator
= 0;
87 dimm
->vdd_voltage
= ddr_get_voltage(dmi17
->MemoryType
);
89 strncpy((char *)dimm
->module_part_number
, dmi17
->PartNumber
,
90 sizeof(dimm
->module_part_number
) - 1);
93 static void print_dimm_info(const struct dimm_info
*dimm
)
99 " max_speed_mts: %hu\n"
100 " config_speed_mts: %hu\n"
101 " vdd_voltage: %hu\n"
102 " rank_per_dimm: %hhu\n"
103 " channel_num: %hhu\n"
105 " bank_locator: %hhu\n"
107 " mod_type: 0x%hhx\n"
109 " serial: %02hhx%02hhx%02hhx%02hhx\n"
110 " module_part_number(%zu): %s\n",
114 dimm
->configured_speed_mts
,
127 strlen((const char *)dimm
->module_part_number
),
128 (char *)dimm
->module_part_number
);
131 static void print_dmi_info(const TYPE17_DMI_INFO
*dmi17
)
134 "AGESA TYPE 17 DMI INFO:\n"
141 " ManufacturerIdCode: %llx\n"
142 " Attributes: %hhu\n"
144 " ConfigSpeed: %hu\n"
145 " MemoryType: 0x%x\n"
146 " FormFactor: 0x%x\n"
147 " DeviceLocator: %8s\n"
148 " BankLocator: %10s\n"
149 " SerialNumber(%zu): %9s\n"
150 " PartNumber(%zu): %19s\n",
157 dmi17
->ManufacturerIdCode
,
163 dmi17
->DeviceLocator
,
165 strlen((const char *)dmi17
->SerialNumber
),
167 strlen((const char *)dmi17
->PartNumber
),
172 * Marshalls dimm info from AMD_FSP_DMI_HOB into CBMEM_ID_MEMINFO
174 static void prepare_dmi_16_17(void *unused
)
176 const DMI_INFO
*dmi_table
;
177 const TYPE17_DMI_INFO
*type17_dmi_info
;
178 struct memory_info
*mem_info
;
179 struct dimm_info
*dimm_info
;
180 char cbi_part_number
[DIMM_INFO_PART_NUMBER_SIZE
];
181 bool use_cbi_part_number
= false;
183 size_t amd_fsp_dmi_hob_size
;
184 const EFI_GUID amd_fsp_dmi_hob_guid
= AMD_FSP_DMI_HOB_GUID
;
186 printk(BIOS_DEBUG
, "Saving dimm info for smbios type 17\n");
188 /* Allocate meminfo in cbmem. */
189 mem_info
= cbmem_add(CBMEM_ID_MEMINFO
, sizeof(struct memory_info
));
192 "Failed to add memory info to CBMEM, DMI tables will be incomplete\n");
195 memset(mem_info
, 0, sizeof(struct memory_info
));
197 /* Locate the memory info HOB. */
198 dmi_table
= fsp_find_extension_hob_by_guid(
199 (const uint8_t *)&amd_fsp_dmi_hob_guid
, &amd_fsp_dmi_hob_size
);
201 if (dmi_table
== NULL
|| amd_fsp_dmi_hob_size
== 0) {
203 "AMD_FSP_DMI_HOB not found, DMI table 17 will be incomplete\n");
206 printk(BIOS_DEBUG
, "AMD_FSP_DMI_HOB found\n");
208 if (CONFIG(EC_GOOGLE_CHROMEEC
)) {
209 /* Prefer DRAM part number from CBI. */
210 if (google_chromeec_cbi_get_dram_part_num(
211 cbi_part_number
, sizeof(cbi_part_number
)) == 0) {
212 use_cbi_part_number
= true;
214 printk(BIOS_ERR
, "Could not obtain DRAM part number from CBI\n");
218 for (unsigned int channel
= 0; channel
< AGESA_STRUCT_CHANNELS_PER_SOCKET
; channel
++) {
219 for (unsigned int dimm
= 0; dimm
< AGESA_STRUCT_DIMMS_PER_CHANNEL
; dimm
++) {
220 type17_dmi_info
= &dmi_table
->T17
[0][channel
][dimm
];
221 /* DIMMs that are present will have a non-zero
223 if (type17_dmi_info
->Handle
== 0)
225 print_dmi_info(type17_dmi_info
);
226 dimm_info
= &mem_info
->dimm
[dimm_cnt
];
227 dimm_info
->channel_num
= channel
;
228 dimm_info
->dimm_num
= dimm
;
229 transfer_memory_info(type17_dmi_info
, dimm_info
);
230 if (use_cbi_part_number
) {
231 /* mem_info is memset to 0 above, so it's
232 safe to assume module_part_number will be
234 strncpy((char *)dimm_info
->module_part_number
, cbi_part_number
,
235 sizeof(dimm_info
->module_part_number
) - 1);
237 /* These ID values match what's used in device/dram/spd.c */
238 switch (dimm_info
->module_part_number
[0]) {
240 dimm_info
->mod_id
= 0xad00; // Hynix
243 dimm_info
->mod_id
= 0x9801; // Kingston
246 dimm_info
->mod_id
= 0x2c00; // Micron
249 dimm_info
->mod_id
= 0x0b83; // Nanya
253 print_dimm_info(dimm_info
);
257 mem_info
->dimm_cnt
= dimm_cnt
;
259 mem_info
->ecc_type
= dmi_table
->T16
.MemoryErrorCorrection
;
262 /* AMD_FSP_DMI_HOB is initialized very late, so check it just in time for writing tables. */
263 BOOT_STATE_INIT_ENTRY(BS_WRITE_TABLES
, BS_ON_ENTRY
, prepare_dmi_16_17
, NULL
);