blob: 21a298e88ac9991131bf8ee7ce8a85fdf74cff6c [file] [log] [blame]
Angel Pons2de6bdf2020-04-05 13:21:00 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Lee Leahy89b5fbd2015-05-11 17:24:31 -07002
3#include <cbfs.h>
4#include <cbmem.h>
5#include <console/console.h>
Subrata Banike5e94392015-08-22 11:10:22 +05306#include <gpio.h>
Lee Leahy89b5fbd2015-05-11 17:24:31 -07007#include <lib.h>
8#include <memory_info.h>
9#include <smbios.h>
10#include <spd.h>
11#include <soc/gpio.h>
12#include <soc/romstage.h>
13#include <string.h>
Matt DeVillier869f22f2017-08-24 14:47:28 -050014#include <spd_bin.h>
Matt DeVilliere69a9c72017-08-20 14:48:57 -050015#include "spd_util.h"
Lee Leahy89b5fbd2015-05-11 17:24:31 -070016
Aaron Durbin64031672018-04-21 14:45:32 -060017__weak uint8_t get_ramid(void)
Lee Leahy89b5fbd2015-05-11 17:24:31 -070018{
Subrata Banike5e94392015-08-22 11:10:22 +053019 gpio_t spd_gpios[] = {
Matt DeVillier869f22f2017-08-24 14:47:28 -050020 GP_SW_80, /* SATA_GP3, RAMID0 */
21 GP_SW_67, /* I2C3_SCL, RAMID1 */
Subrata Banike5e94392015-08-22 11:10:22 +053022 GP_SE_02, /* MF_PLT_CLK1, RAMID2 */
Matt DeVillier869f22f2017-08-24 14:47:28 -050023 GP_SW_64, /* I2C3_SDA, RAMID3 */
Subrata Banike5e94392015-08-22 11:10:22 +053024 };
25
Matt DeVilliere69a9c72017-08-20 14:48:57 -050026 return gpio_base2_value(spd_gpios, ARRAY_SIZE(spd_gpios));
27}
28
Nico Huber85f0b052019-05-04 17:06:06 +020029static void *get_spd_pointer(int *dual)
Matt DeVilliere69a9c72017-08-20 14:48:57 -050030{
Nico Huber85f0b052019-05-04 17:06:06 +020031 char *spd_file;
32 size_t spd_file_len;
33 int total_spds;
Matt DeVilliere69a9c72017-08-20 14:48:57 -050034 int ram_id = 0;
35 int spd_index = 0;
36
Nico Huber85f0b052019-05-04 17:06:06 +020037 /* Find the SPD data in CBFS. */
Julius Werner834b3ec2020-03-04 16:52:08 -080038 spd_file = cbfs_map("spd.bin", &spd_file_len);
Nico Huber85f0b052019-05-04 17:06:06 +020039 if (!spd_file)
40 die("SPD data not found.");
41
42 if (spd_file_len < SPD_PAGE_LEN)
43 die("Missing SPD data.");
44 total_spds = spd_file_len / SPD_PAGE_LEN;
45
Matt DeVilliere69a9c72017-08-20 14:48:57 -050046 ram_id = get_ramid();
Lee Leahy89b5fbd2015-05-11 17:24:31 -070047 printk(BIOS_DEBUG, "ram_id=%d, total_spds: %d\n", ram_id, total_spds);
Matt DeVilliere69a9c72017-08-20 14:48:57 -050048
49 spd_index = get_variant_spd_index(ram_id, dual);
50 if (spd_index >= total_spds) {
51 printk(BIOS_ERR, "SPD index > total SPDs\n");
Lee Leahy89b5fbd2015-05-11 17:24:31 -070052 return NULL;
Lee Leahy89b5fbd2015-05-11 17:24:31 -070053 }
Lee Leahy89b5fbd2015-05-11 17:24:31 -070054 /* Return the serial product data for the RAM */
Nico Huber85f0b052019-05-04 17:06:06 +020055 return &spd_file[SPD_PAGE_LEN * spd_index];
Lee Leahy89b5fbd2015-05-11 17:24:31 -070056}
57
58/* Copy SPD data for on-board memory */
Nico Huber85f0b052019-05-04 17:06:06 +020059void spd_memory_init_params(MEMORY_INIT_UPD *memory_params)
Lee Leahy89b5fbd2015-05-11 17:24:31 -070060{
Lee Leahy89b5fbd2015-05-11 17:24:31 -070061 void *spd_content;
62 int dual_channel = 0;
63
Lee Leahy89b5fbd2015-05-11 17:24:31 -070064 /*
65 * Both channels are always present in SPD data. Always use matched
66 * DIMMs so use the same SPD data for each DIMM.
67 */
Nico Huber85f0b052019-05-04 17:06:06 +020068 spd_content = get_spd_pointer(&dual_channel);
Julius Wernercd49cce2019-03-05 16:53:33 -080069 if (CONFIG(DISPLAY_SPD_DATA) && spd_content != NULL) {
Lee Leahy89b5fbd2015-05-11 17:24:31 -070070 printk(BIOS_DEBUG, "SPD Data:\n");
Matt DeVillier869f22f2017-08-24 14:47:28 -050071 hexdump(spd_content, SPD_PAGE_LEN);
Lee Leahy89b5fbd2015-05-11 17:24:31 -070072 printk(BIOS_DEBUG, "\n");
73 }
74
75 /*
76 * Set SPD and memory configuration:
77 * Memory type: 0=DimmInstalled,
78 * 1=SolderDownMemory,
79 * 2=DimmDisabled
80 */
81 if (spd_content != NULL) {
Nico Huber85f0b052019-05-04 17:06:06 +020082 memory_params->PcdMemChannel0Config = 1;
Lee Leahy89b5fbd2015-05-11 17:24:31 -070083 printk(BIOS_DEBUG, "Channel 0 DIMM soldered down\n");
84 if (dual_channel) {
85 printk(BIOS_DEBUG, "Channel 1 DIMM soldered down\n");
Nico Huber85f0b052019-05-04 17:06:06 +020086 memory_params->PcdMemChannel1Config = 1;
Lee Leahy89b5fbd2015-05-11 17:24:31 -070087 } else {
88 printk(BIOS_DEBUG, "Channel 1 DIMM not installed\n");
Nico Huber85f0b052019-05-04 17:06:06 +020089 memory_params->PcdMemChannel1Config = 2;
Lee Leahy89b5fbd2015-05-11 17:24:31 -070090 }
91 }
Nico Huber85f0b052019-05-04 17:06:06 +020092
93 /* Update SPD data */
94 if (CONFIG(BOARD_GOOGLE_CYAN)) {
95 memory_params->PcdMemoryTypeEnable = MEM_DDR3;
96 memory_params->PcdMemorySpdPtr = (uintptr_t)spd_content;
97 } else {
98 memory_params->PcdMemoryTypeEnable = MEM_LPDDR3;
99 }
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700100}
101
Nico Huber85f0b052019-05-04 17:06:06 +0200102static void set_dimm_info(const uint8_t *spd, struct dimm_info *dimm)
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700103{
Matt DeVillier869f22f2017-08-24 14:47:28 -0500104 const int spd_capmb[8] = { 1, 2, 4, 8, 16, 32, 64, 0 };
105 const int spd_ranks[8] = { 1, 2, 3, 4, -1, -1, -1, -1 };
106 const int spd_devw[8] = { 4, 8, 16, 32, -1, -1, -1, -1 };
107 const int spd_busw[8] = { 8, 16, 32, 64, -1, -1, -1, -1 };
108
109 int capmb = spd_capmb[spd[SPD_DENSITY_BANKS] & 7] * 256;
Eric Laiaa8d7722019-09-02 15:01:56 +0800110 int ranks = spd_ranks[(spd[DDR3_ORGANIZATION] >> 3) & 7];
111 int devw = spd_devw[spd[DDR3_ORGANIZATION] & 7];
112 int busw = spd_busw[spd[DDR3_BUS_DEV_WIDTH] & 7];
Matt DeVillier869f22f2017-08-24 14:47:28 -0500113
114 void *hob_list_ptr;
115 EFI_HOB_GUID_TYPE *hob_ptr;
116 FSP_SMBIOS_MEMORY_INFO *memory_info_hob;
117 const EFI_GUID memory_info_hob_guid = FSP_SMBIOS_MEMORY_INFO_GUID;
118
119 /* Locate the memory info HOB, presence validated by raminit */
120 hob_list_ptr = fsp_get_hob_list();
121 hob_ptr = get_next_guid_hob(&memory_info_hob_guid, hob_list_ptr);
122 if (hob_ptr != NULL) {
123 memory_info_hob = (FSP_SMBIOS_MEMORY_INFO *)(hob_ptr + 1);
124 dimm->ddr_frequency = memory_info_hob->MemoryFrequencyInMHz;
125 } else {
126 printk(BIOS_ERR, "Can't get memory info hob pointer\n");
127 dimm->ddr_frequency = 0;
128 }
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700129
130 /* Parse the SPD data to determine the DIMM information */
Julius Wernercd49cce2019-03-05 16:53:33 -0800131 if (CONFIG(BOARD_GOOGLE_CYAN)) {
Elyes HAOUAS28114ae2018-11-14 17:51:00 +0100132 dimm->ddr_type = MEMORY_TYPE_DDR3;
Matt DeVillier4f20a4a2017-08-20 17:56:48 -0500133 } else {
Elyes HAOUAS28114ae2018-11-14 17:51:00 +0100134 dimm->ddr_type = MEMORY_TYPE_LPDDR3;
Matt DeVillier4f20a4a2017-08-20 17:56:48 -0500135 }
Matt DeVillier869f22f2017-08-24 14:47:28 -0500136 dimm->dimm_size = capmb / 8 * busw / devw * ranks; /* MiB */
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700137 dimm->mod_type = spd[3] & 0xf;
Richard Spiegel90b30952018-04-17 10:09:17 -0700138 strncpy((char *)&dimm->module_part_number[0], (char *)&spd[0x80],
139 LPDDR3_SPD_PART_LEN);
140 dimm->module_part_number[LPDDR3_SPD_PART_LEN] = 0;
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700141 dimm->mod_id = *(uint16_t *)&spd[0x94];
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700142
Matt DeVillier869f22f2017-08-24 14:47:28 -0500143 switch (busw) {
Ravi Sarawadid077b582015-09-09 14:12:16 -0700144 default:
Matt DeVillier869f22f2017-08-24 14:47:28 -0500145 case 8:
146 dimm->bus_width = MEMORY_BUS_WIDTH_8;
147 break;
148
149 case 16:
150 dimm->bus_width = MEMORY_BUS_WIDTH_16;
151 break;
152
153 case 32:
154 dimm->bus_width = MEMORY_BUS_WIDTH_32;
155 break;
156
157 case 64:
158 dimm->bus_width = MEMORY_BUS_WIDTH_64;
159 break;
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700160 }
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700161}
162
163void mainboard_save_dimm_info(struct romstage_params *params)
164{
Nico Huber85f0b052019-05-04 17:06:06 +0200165 const void *spd_content;
166 int dual_channel;
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700167 struct dimm_info *dimm;
168 struct memory_info *mem_info;
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700169
Nico Huber85f0b052019-05-04 17:06:06 +0200170 spd_content = get_spd_pointer(&dual_channel);
171 if (spd_content == NULL)
172 return;
173
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700174 /*
175 * Allocate CBMEM area for DIMM information used to populate SMBIOS
176 * table 17
177 */
178 mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(*mem_info));
Julius Werner540a9802019-12-09 13:03:29 -0800179 printk(BIOS_DEBUG, "CBMEM entry for DIMM info: %p\n", mem_info);
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700180 if (mem_info == NULL)
181 return;
182 memset(mem_info, 0, sizeof(*mem_info));
183
184 /* Describe the first channel memory */
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700185 dimm = &mem_info->dimm[0];
Nico Huber85f0b052019-05-04 17:06:06 +0200186 set_dimm_info(spd_content, dimm);
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700187 mem_info->dimm_cnt = 1;
188
189 /* Describe the second channel memory */
Nico Huber85f0b052019-05-04 17:06:06 +0200190 if (dual_channel) {
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700191 dimm = &mem_info->dimm[1];
Nico Huber85f0b052019-05-04 17:06:06 +0200192 set_dimm_info(spd_content, dimm);
Lee Leahy89b5fbd2015-05-11 17:24:31 -0700193 dimm->channel_num = 1;
194 mem_info->dimm_cnt = 2;
195 }
196}