blob: 0dd995447f891d0aab64a6fab7070df9b915f504 [file] [log] [blame]
jinuthomas6555e7e2023-02-14 21:48:00 -06001#include "isdimm_vpd_parser.hpp"
2
3#include <iostream>
4#include <numeric>
5#include <string>
6
7namespace openpower
8{
9namespace vpd
10{
jinuthomas5700b3c2023-03-07 22:51:00 -060011namespace isdimm
jinuthomas6555e7e2023-02-14 21:48:00 -060012{
13namespace parser
14{
15static constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F;
16static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07;
17static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07;
18static constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38;
19static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70;
20static constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02;
21static constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03;
22
23static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256;
24static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8;
25static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4;
26static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8;
27static constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4;
28static constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3;
29static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4;
30
31static constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321;
32static constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320;
33static constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325;
34static constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326;
35static constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327;
36static constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328;
37static constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4;
38static constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5;
39static constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6;
40static constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12;
41
42// DDR5 JEDEC specification constants
43static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM = 235;
44static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM_MASK = 0x60;
45static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL = 235;
46static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL_MASK = 0x07;
47static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_SYM_ALL = 6;
48static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_EVEN = 6;
49static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_ODD = 10;
50static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_MASK = 0xE0;
51static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_SYM_ALL = 4;
52static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_EVEN = 4;
53static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_ODD = 8;
54static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_MASK = 0xE0;
55static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_SYM_ALL = 4;
56static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_EVEN = 4;
57static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_ODD = 8;
58static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_MASK = 0x1F;
59static constexpr auto SPD_JEDEC_DDR5_RANK_MIX = 234;
60static constexpr auto SPD_JEDEC_DDR5_RANK_MIX_SYMMETRICAL_MASK = 0x40;
61
62auto isdimmVpdParser::getDDR4DimmCapacity(Binary::const_iterator& iterator)
63{
64 size_t tmp = 0, dimmSize = 0;
65
66 size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1;
67 Byte dieCount = 1;
68
69 // NOTE: This calculation is Only for DDR4
70
71 // Calculate SDRAM capacity
72 tmp = iterator[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK;
73 /* Make sure the bits are not Reserved */
74 if (tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED)
75 {
76 std::cerr
77 << "Bad data in spd byte 4. Can't calculate SDRAM capacity and so "
78 "dimm size.\n ";
79 return dimmSize;
80 }
81
82 sdramCap = (sdramCap << tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER;
83
84 /* Calculate Primary bus width */
85 tmp = iterator[constants::SPD_BYTE_13] & SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK;
86 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
87 {
88 std::cerr
89 << "Bad data in spd byte 13. Can't calculate primary bus width "
90 "and so dimm size.\n ";
91 return dimmSize;
92 }
93 priBusWid = (priBusWid << tmp) * SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER;
94
95 /* Calculate SDRAM width */
96 tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK;
97 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
98 {
99 std::cerr
100 << "Bad data in vpd byte 12. Can't calculate SDRAM width and so "
101 "dimm size.\n ";
102 return dimmSize;
103 }
104 sdramWid = (sdramWid << tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER;
105
106 tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK;
107
108 if (tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK)
109 {
110 // Fetch die count
111 tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_DIE_COUNT_MASK;
112 tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT;
113 dieCount = tmp + 1;
114 }
115
116 /* Calculate Number of ranks */
117 tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK;
118 tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS;
119
120 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
121 {
122 std::cerr << "Can't calculate number of ranks. Invalid data found.\n ";
123 return dimmSize;
124 }
125 logicalRanksPerDimm = (tmp + 1) * dieCount;
126
127 dimmSize = (sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) *
128 (priBusWid / sdramWid) * logicalRanksPerDimm;
129
130 return dimmSize;
131}
132
133auto isdimmVpdParser::getDDR4PartNumber(Binary::const_iterator& iterator)
134{
jinuthomas6555e7e2023-02-14 21:48:00 -0600135 char tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
136 sprintf(tmpPN, "%02X%02X%02X%X",
137 iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
138 iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
139 iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
140 iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
jinuthomasd640f692023-03-28 04:13:23 -0500141 std::string partNumber(tmpPN, sizeof(tmpPN) - 1);
jinuthomas6555e7e2023-02-14 21:48:00 -0600142 return partNumber;
143}
144
145auto isdimmVpdParser::getDDR4SerialNumber(Binary::const_iterator& iterator)
146{
147 char tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
148 sprintf(tmpSN, "%02X%02X%02X%02X%02X%02X",
149 iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
150 iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
151 iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
152 iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
153 iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
154 iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
jinuthomasd640f692023-03-28 04:13:23 -0500155 std::string serialNumber(tmpSN, sizeof(tmpSN) - 1);
jinuthomas6555e7e2023-02-14 21:48:00 -0600156 return serialNumber;
157}
158
jinuthomas680960e2023-05-17 05:58:50 -0500159auto isdimmVpdParser::getDDR4FruNumber(const std::string& partNumber,
160 Binary::const_iterator& iterator)
jinuthomas6555e7e2023-02-14 21:48:00 -0600161{
162 // check for 128GB ISRDIMM not implemented
163 //(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA
164
jinuthomas680960e2023-05-17 05:58:50 -0500165 // MTB Units is used in deciding the frequency of the DIMM
166 // This is applicable only for DDR4 specification
167 // 10 - DDR4-1600
168 // 9 - DDR4-1866
169 // 8 - DDR4-2133
170 // 7 - DDR4-2400
171 // 6 - DDR4-2666
172 // 5 - DDR4-3200
173 // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
174 static std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap =
175 {{std::make_tuple("8421000", 6), "78P4191"},
176 {std::make_tuple("8421008", 6), "78P4192"},
177 {std::make_tuple("8529000", 6), "78P4197"},
178 {std::make_tuple("8529008", 6), "78P4198"},
179 {std::make_tuple("8529928", 6), "78P4199"},
180 {std::make_tuple("8529B28", 6), "78P4200"},
181 {std::make_tuple("8631928", 6), "78P6925"},
182 {std::make_tuple("8529000", 5), "78P7317"},
183 {std::make_tuple("8529008", 5), "78P7318"},
184 {std::make_tuple("8631008", 5), "78P6815"}};
jinuthomas6555e7e2023-02-14 21:48:00 -0600185
186 std::string fruNumber;
jinuthomas680960e2023-05-17 05:58:50 -0500187 uint8_t mtbUnits = iterator[openpower::vpd::constants::SPD_BYTE_18] &
188 openpower::vpd::constants::SPD_BYTE_MASK;
189 std::tuple<std::string, uint8_t> tup_key = {partNumber, mtbUnits};
190 auto itr = pnFreqFnMap.find(tup_key);
191 if (itr != pnFreqFnMap.end())
jinuthomas6555e7e2023-02-14 21:48:00 -0600192 {
193 fruNumber = itr->second;
194 }
195 else
196 {
197 fruNumber = "FFFFFFF";
198 }
199 return fruNumber;
200}
201
jinuthomas680960e2023-05-17 05:58:50 -0500202auto isdimmVpdParser::getDDR4CCIN(const std::string& fruNumber)
jinuthomas6555e7e2023-02-14 21:48:00 -0600203{
204 static std::unordered_map<std::string, std::string> pnCCINMap = {
jinuthomas680960e2023-05-17 05:58:50 -0500205 {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
206 {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
207 {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
208 {"78P6815", "32BB"}};
jinuthomas6555e7e2023-02-14 21:48:00 -0600209
210 std::string ccin;
jinuthomas680960e2023-05-17 05:58:50 -0500211 auto itr = pnCCINMap.find(fruNumber);
jinuthomas6555e7e2023-02-14 21:48:00 -0600212 if (itr != pnCCINMap.end())
213 {
214 ccin = itr->second;
215 }
216 else
217 {
218 ccin = "XXXX";
219 }
220 return ccin;
221}
222
223auto isdimmVpdParser::getDDR5DimmCapacity(Binary::const_iterator& iterator)
224{
225 // dummy implementation to be updated when required
226 size_t dimmSize = 0;
227 (void)iterator;
228 return dimmSize;
229}
230
231auto isdimmVpdParser::getDDR5PartNumber(Binary::const_iterator& iterator)
232{
233 // dummy implementation to be updated when required
234 std::string partNumber;
235 (void)iterator;
236 partNumber = "0123456";
237 return partNumber;
238}
239
240auto isdimmVpdParser::getDDR5SerialNumber(Binary::const_iterator& iterator)
241{
242 // dummy implementation to be updated when required
243 std::string serialNumber;
244 (void)iterator;
245 serialNumber = "444444444444";
246 return serialNumber;
247}
248
249auto isdimmVpdParser::getDDR5FruNumber(const std::string& partNumber)
250{
251 // dummy implementation to be updated when required
252 static std::unordered_map<std::string, std::string> pnFruMap = {
253 {"1234567", "XXXXXXX"}};
254
255 std::string fruNumber;
256 auto itr = pnFruMap.find(partNumber);
257 if (itr != pnFruMap.end())
258 {
259 fruNumber = itr->second;
260 }
261 else
262 {
263 fruNumber = "FFFFFFF";
264 }
265 return fruNumber;
266}
267
268auto isdimmVpdParser::getDDR5CCIN(const std::string& partNumber)
269{
270 // dummy implementation to be updated when required
271 static std::unordered_map<std::string, std::string> pnCCINMap = {
272 {"1234567", "XXXX"}};
273
274 std::string ccin;
275 auto itr = pnCCINMap.find(partNumber);
276 if (itr != pnCCINMap.end())
277 {
278 ccin = itr->second;
279 }
280 else
281 {
282 ccin = "XXXX";
283 }
284 return ccin;
285}
286
287kwdVpdMap isdimmVpdParser::readKeywords(Binary::const_iterator& iterator)
288{
289 inventory::KeywordVpdMap keywordValueMap{};
290 if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
291 constants::SPD_DRAM_TYPE_DDR5)
292 {
jinuthomasd640f692023-03-28 04:13:23 -0500293 size_t dimmSize = getDDR5DimmCapacity(iterator);
jinuthomas6555e7e2023-02-14 21:48:00 -0600294 if (!dimmSize)
295 {
296 std::cerr << "Error: Calculated dimm size is 0.";
297 }
jinuthomas6555e7e2023-02-14 21:48:00 -0600298 else
299 {
jinuthomasd640f692023-03-28 04:13:23 -0500300 keywordValueMap.emplace("MemorySizeInKB", dimmSize);
jinuthomas6555e7e2023-02-14 21:48:00 -0600301 }
302 auto partNumber = getDDR5PartNumber(iterator);
jinuthomas6555e7e2023-02-14 21:48:00 -0600303 auto fruNumber = getDDR5FruNumber(partNumber);
304 keywordValueMap.emplace("FN", move(fruNumber));
305 auto serialNumber = getDDR5SerialNumber(iterator);
306 keywordValueMap.emplace("SN", move(serialNumber));
307 auto ccin = getDDR5CCIN(partNumber);
308 keywordValueMap.emplace("CC", move(ccin));
jinuthomasd640f692023-03-28 04:13:23 -0500309 keywordValueMap.emplace("PN", move(partNumber));
jinuthomas6555e7e2023-02-14 21:48:00 -0600310 }
311 else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
312 constants::SPD_DRAM_TYPE_DDR4)
313 {
jinuthomasd640f692023-03-28 04:13:23 -0500314 size_t dimmSize = getDDR4DimmCapacity(iterator);
jinuthomas6555e7e2023-02-14 21:48:00 -0600315 if (!dimmSize)
316 {
317 std::cerr << "Error: Calculated dimm size is 0.";
318 }
jinuthomas6555e7e2023-02-14 21:48:00 -0600319 else
320 {
jinuthomasd640f692023-03-28 04:13:23 -0500321 keywordValueMap.emplace("MemorySizeInKB",
322 (dimmSize * constants::CONVERT_MB_TO_KB));
jinuthomas6555e7e2023-02-14 21:48:00 -0600323 }
jinuthomasd640f692023-03-28 04:13:23 -0500324
jinuthomas6555e7e2023-02-14 21:48:00 -0600325 auto partNumber = getDDR4PartNumber(iterator);
jinuthomas680960e2023-05-17 05:58:50 -0500326 auto fruNumber = getDDR4FruNumber(partNumber, iterator);
jinuthomas6555e7e2023-02-14 21:48:00 -0600327 auto serialNumber = getDDR4SerialNumber(iterator);
jinuthomas680960e2023-05-17 05:58:50 -0500328 auto ccin = getDDR4CCIN(fruNumber);
jinuthomasd640f692023-03-28 04:13:23 -0500329 keywordValueMap.emplace("PN", move(partNumber));
330 keywordValueMap.emplace("FN", move(fruNumber));
331 keywordValueMap.emplace("SN", move(serialNumber));
jinuthomas6555e7e2023-02-14 21:48:00 -0600332 keywordValueMap.emplace("CC", move(ccin));
333 }
334 return keywordValueMap;
335}
336
337std::variant<kwdVpdMap, Store> isdimmVpdParser::parse()
338{
339 // Read the data and return the map
340 auto iterator = memVpd.cbegin();
341 auto vpdDataMap = readKeywords(iterator);
342
343 return vpdDataMap;
344}
345
346} // namespace parser
jinuthomas5700b3c2023-03-07 22:51:00 -0600347} // namespace isdimm
jinuthomas6555e7e2023-02-14 21:48:00 -0600348} // namespace vpd
jinuthomas5700b3c2023-03-07 22:51:00 -0600349} // namespace openpower