blob: ee7622143791be1e6786244f0be723fef01deaf3 [file] [log] [blame]
David Hendricksbf2bbaa2014-10-30 15:25:51 -07001/* Copyright 2014, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * nonspd.c: Functions for pretty printing memory info for systems without SPD.
31 */
32
33#include <ctype.h>
34#include <stdint.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
38
David Hendricksbf2bbaa2014-10-30 15:25:51 -070039#include "jedec_id.h"
40#include "lib/math.h"
41#include "lib/nonspd.h"
42#include "lib/string.h"
Jack Rosenthaldf9917b2020-10-21 10:57:39 -060043#include "lib/val2str.h"
David Hendricksbf2bbaa2014-10-30 15:25:51 -070044#include "mosys/platform.h"
45#include "mosys/kv_pair.h"
46#include "mosys/log.h"
47
48/*
49 * nonspd_print_field - add common DDR SPD fields into key=value pair
50 *
51 * @kv: key=value pair
52 * @info: nonspd memory info
53 * @type: type of field to retrieve
54 *
55 * returns 1 to indicate data added to key=value pair
56 * returns 0 to indicate no data added
57 * returns <0 to indicate error
58 *
59 */
60int nonspd_print_field(struct kv_pair *kv,
61 const struct nonspd_mem_info *info,
62 enum spd_field_type type)
63{
64 int ret = 0;
65
66 switch (type) {
67 case SPD_GET_DRAM_TYPE:
68 switch (info->dram_type) {
David Hendricksbf2bbaa2014-10-30 15:25:51 -070069 case SPD_DRAM_TYPE_DDR3:
70 kv_pair_add(kv, "dram", "DDR3");
71 break;
Furquan Shaikh972aa9d2019-03-08 09:25:16 -080072 case SPD_DRAM_TYPE_DDR4:
73 kv_pair_add(kv, "dram", "DDR4");
74 break;
Paul Fagerburg1f3997c2019-05-17 09:31:29 -060075 case SPD_DRAM_TYPE_JEDEC_LPDDR3:
David Hendricksbf2bbaa2014-10-30 15:25:51 -070076 case SPD_DRAM_TYPE_LPDDR3:
77 kv_pair_add(kv, "dram", "LPDDR3");
78 break;
Ravi Sarawadi7ef277d2016-08-16 17:04:00 -070079 case SPD_DRAM_TYPE_LPDDR4:
80 kv_pair_add(kv, "dram", "LPDDR4");
81 break;
Hsin-Yi, Wangafcacfb2019-01-17 19:23:10 +080082 case SPD_DRAM_TYPE_LPDDR4X:
83 kv_pair_add(kv, "dram", "LPDDR4X");
84 break;
David Hendricksbf2bbaa2014-10-30 15:25:51 -070085 default:
86 break;
87 }
88 ret = 1;
89 break;
90
91 case SPD_GET_MODULE_TYPE:
92 switch (info->dram_type) {
93 case SPD_DRAM_TYPE_DDR3:
Caesar Wangdd8b8462015-01-08 21:35:56 +080094 case SPD_DRAM_TYPE_LPDDR3:
Paul Fagerburg1f3997c2019-05-17 09:31:29 -060095 case SPD_DRAM_TYPE_JEDEC_LPDDR3:
David Hendricksbf2bbaa2014-10-30 15:25:51 -070096 kv_pair_add(kv, "module",
97 val2str(info->module_type.ddr3_type,
98 ddr3_module_type_lut));
99 ret = 1;
100 break;
Furquan Shaikh972aa9d2019-03-08 09:25:16 -0800101 case SPD_DRAM_TYPE_DDR4:
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700102 default:
103 ret = -1;
104 break;
105 }
106 break;
107
108 case SPD_GET_MFG_ID:
109 {
110 uint8_t manuf_lsb = info->module_mfg_id.lsb & 0x7f;
111 uint8_t manuf_msb = info->module_mfg_id.msb & 0x7f;
112 const char *tstr;
113
114 tstr = jedec_manufacturer(manuf_lsb, manuf_msb);
115
116 if (tstr != NULL) {
117 kv_pair_fmt(kv, "module_mfg", "%u-%u: %s", manuf_lsb + 1,
118 manuf_msb, tstr);
119 } else {
120 kv_pair_fmt(kv, "module_mfg", "%u-%u", manuf_lsb + 1,
121 manuf_msb);
122 }
123 ret = 1;
124 break;
125 }
126
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700127 case SPD_GET_PART_NUMBER:
128 {
jiazi Yangcf034022017-04-05 05:43:50 -0400129 char part[sizeof(info->part_num)+1];
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700130
jiazi Yangcf034022017-04-05 05:43:50 -0400131 memcpy(part, &info->part_num[0], sizeof(info->part_num));
132 part[sizeof(info->part_num)] = '\0';
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700133 kv_pair_fmt(kv, "part_number", "%s", part);
134
135 ret = 1;
136 break;
137 }
138
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700139 case SPD_GET_SIZE:
140 {
141 /* translate mbits to mbytes */
142 kv_pair_fmt(kv, "size_mb", "%u", info->module_size_mbits / 8);
143 ret = 1;
144 break;
145 }
146
147 case SPD_GET_RANKS:
148 {
149 kv_pair_fmt(kv, "ranks", "%d", info->num_ranks);
150 ret = 1;
151 break;
152 }
153
154 case SPD_GET_WIDTH:
155 {
156 kv_pair_fmt(kv, "width", "%d", info->device_width);
157 ret = 1;
158 break;
159 }
160
David Hendricksbf2bbaa2014-10-30 15:25:51 -0700161 default:
162 break;
163 }
164
165 return ret;
166}
Hung-Te Lin13f1def2021-04-07 21:18:20 +0800167
168// This one is reserved for storing mem info from SMBIOS if no explicit entry
169// was added above.
170static struct nonspd_mem_info part_extracted_from_smbios = {
171 .part_num =
172 { 'U', 'N', 'P', 'R', 'O', 'V', 'I', 'S', 'I', 'O', 'N', 'E', 'D'},
173};
174
175static enum spd_dram_type map_smbios_mem_type_to_spd(struct smbios_table *table)
176{
177 char *part_number = table->string[table->data.mem_device.part_number];
178 static const struct {
179 enum spd_dram_type type;
180 const char *prefix;
181 } part_number_matches[] = {
182 /* Hynix */
183 { SPD_DRAM_TYPE_DDR3, "h5t" },
184 { SPD_DRAM_TYPE_LPDDR3, "h9c" },
185 { SPD_DRAM_TYPE_LPDDR4, "h9h" },
186
187 /* Samsung */
188 { SPD_DRAM_TYPE_DDR3, "k4b" },
189 { SPD_DRAM_TYPE_LPDDR3, "k3q" },
190 { SPD_DRAM_TYPE_LPDDR3, "k4e" },
191 { SPD_DRAM_TYPE_LPDDR4, "k3u" },
192 { SPD_DRAM_TYPE_LPDDR4, "k4f" },
193
194 /* Micron */
195 { SPD_DRAM_TYPE_DDR4, "mt40" },
196 { SPD_DRAM_TYPE_DDR3, "mt41" },
197 { SPD_DRAM_TYPE_LPDDR3, "mt52" },
198 { SPD_DRAM_TYPE_LPDDR4, "mt53" },
199 };
200
201 switch (table->data.mem_device.type) {
202 case SMBIOS_MEMORY_TYPE_DDR3:
203 return SPD_DRAM_TYPE_DDR3;
204 case SMBIOS_MEMORY_TYPE_DDR4:
205 return SPD_DRAM_TYPE_DDR4;
206 case SMBIOS_MEMORY_TYPE_LPDDR3:
207 return SPD_DRAM_TYPE_LPDDR3;
208 case SMBIOS_MEMORY_TYPE_LPDDR4:
209 return SPD_DRAM_TYPE_LPDDR4;
210 case SMBIOS_MEMORY_TYPE_OTHER:
211 case SMBIOS_MEMORY_TYPE_UNKNOWN:
212 /* Do our best to figure it out from part numbers */
213 for (size_t i = 0; i < ARRAY_SIZE(part_number_matches); i++) {
214 if (!strncasecmp(part_number,
215 part_number_matches[i].prefix,
216 strlen(part_number_matches[i].prefix)))
217 return part_number_matches[i].type;
218 }
219
220 /* Fall thru */
221 default:
222 lprintf(LOG_ERR, "%s: Unknown SMBIOS memory type: %d\n",
223 __func__, table->data.mem_device.type);
224 return 0;
225 }
226}
227
228static int extract_mem_info_from_smbios(
229 struct smbios_table *table,
230 struct nonspd_mem_info *info)
231{
232 const char *smbios_part_num;
233 size_t smbios_part_num_len, max_part_num_len;
234 uint32_t size;
235
236 max_part_num_len = sizeof(info->part_num) - 1;
237 smbios_part_num = table->string[table->data.mem_device.part_number];
238 smbios_part_num_len = strlen(smbios_part_num);
239
240 if (!smbios_part_num_len ||
241 smbios_part_num_len > max_part_num_len) {
242 lprintf(LOG_ERR, "%s: SMBIOS Memory info table: part num is missing. "
243 "Or len of part number %lu is larger then buffer %lu."
244 , __func__, (unsigned long)smbios_part_num_len,
245 (unsigned long)max_part_num_len);
246 return -1;
247 }
248
249 size = (table->data.mem_device.size & 0x7fff) * 8;
250 info->module_size_mbits =
251 (table->data.mem_device.size & 0x8000 ? size * 1024 : size);
252
253 strncpy((char *)info->part_num, smbios_part_num, max_part_num_len);
254
255 info->dram_type = map_smbios_mem_type_to_spd(table);
256 info->num_ranks = table->data.mem_device.attributes & 0xf;
257 info->device_width = table->data.mem_device.data_width;
258
259 return 0;
260}
261
262
263int spd_set_nonspd_info_from_smbios(struct platform_intf *intf, int dimm,
264 const struct nonspd_mem_info **info)
265{
266 struct smbios_table table;
267
268 if (smbios_find_table(intf, SMBIOS_TYPE_MEMORY, dimm, &table) < 0) {
269 lprintf(LOG_ERR, "%s: SMBIOS Memory info table missing\n",
270 __func__);
271 return -1;
272 }
273
274 /* memory device from SMBIOS is mapped into a nonspd_mem_info */
275 if (extract_mem_info_from_smbios(&table, &part_extracted_from_smbios))
276 return -1;
277
278 *info = &part_extracted_from_smbios;
279
280 return 0;
281}