1 #include "mp297x.hpp"
2
3 #include "common/include/utils.hpp"
4
5 #include <phosphor-logging/lg2.hpp>
6
7 #include <fstream>
8
9 PHOSPHOR_LOG2_USING;
10
11 namespace phosphor::software::VR
12 {
13
14 static constexpr size_t statusByteLength = 1;
15
16 static constexpr std::string_view crcUserRegName = "CRC_USER";
17 static constexpr std::string_view crcMultiRegName = "CRC_MULTI";
18
19 enum class MP297XCmd : uint8_t
20 {
21 // Page 0 commands
22 storeDataIntoMTP = 0xF1,
23
24 // Page 1 commands
25 writeProtectMode = 0x35,
26 enableMTPPageWR = 0x4F,
27
28 // Page 2 commands
29 enableMultiConfigCRC = 0xF3,
30
31 // Page 0x29 commands
32 readUserCodeCRC = 0xFF,
33
34 // Page 0x2A commands
35 readMultiConfigCRC = 0xBF,
36 };
37
parseDeviceConfiguration()38 sdbusplus::async::task<bool> MP297X::parseDeviceConfiguration()
39 {
40 if (!configuration)
41 {
42 error("Device configuration not initialized");
43 co_return false;
44 }
45
46 configuration->vendorId = 0x0025;
47 configuration->productId = 0x0071;
48
49 for (const auto& tokens : parser->lineTokens)
50 {
51 if (!parser->isValidDataTokens(tokens))
52 {
53 continue;
54 }
55
56 auto regName = parser->getVal<std::string>(tokens, ATE::regName);
57
58 if (regName == crcUserRegName)
59 {
60 configuration->configId =
61 parser->getVal<uint32_t>(tokens, ATE::configId);
62 configuration->crcUser =
63 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
64 }
65 else if (regName == crcMultiRegName)
66 {
67 configuration->crcMulti =
68 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
69 break;
70 }
71 }
72
73 co_return true;
74 }
75
verifyImage(const uint8_t * image,size_t imageSize)76 sdbusplus::async::task<bool> MP297X::verifyImage(const uint8_t* image,
77 size_t imageSize)
78 {
79 if (!co_await parseImage(image, imageSize))
80 {
81 error("Image verification failed: image parsing failed");
82 co_return false;
83 }
84
85 if (configuration->registersData.empty())
86 {
87 error("Image verification failed - no data found");
88 co_return false;
89 }
90
91 if (configuration->configId == 0)
92 {
93 error("Image verification failed - missing config ID");
94 co_return false;
95 }
96
97 co_return true;
98 }
99
checkId(PMBusCmd idCmd,uint32_t expected)100 sdbusplus::async::task<bool> MP297X::checkId(PMBusCmd idCmd, uint32_t expected)
101 {
102 constexpr size_t idLength = 2;
103
104 std::vector<uint8_t> tbuf;
105 std::vector<uint8_t> rbuf;
106
107 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
108 if (!i2cInterface.sendReceive(tbuf, rbuf))
109 {
110 error("Failed to set page 0 for ID check");
111 co_return false;
112 }
113
114 tbuf = buildByteVector(idCmd);
115 rbuf.resize(statusByteLength + idLength);
116 if (!i2cInterface.sendReceive(tbuf, rbuf))
117 {
118 error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex,
119 static_cast<uint8_t>(idCmd));
120 co_return false;
121 }
122
123 auto idBytes = std::span(rbuf).subspan(statusByteLength);
124 auto id = bytesToInt<uint32_t>(idBytes);
125
126 if (id != expected)
127 {
128 error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
129 lg2::hex, static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP",
130 lg2::hex, expected);
131 co_return false;
132 }
133
134 co_return true;
135 }
136
isPasswordUnlock()137 sdbusplus::async::task<bool> MP297X::isPasswordUnlock()
138 {
139 constexpr uint8_t passwordMatchMask = 0x08;
140
141 std::vector<uint8_t> tbuf;
142 std::vector<uint8_t> rbuf;
143
144 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
145 if (!i2cInterface.sendReceive(tbuf, rbuf))
146 {
147 error("Failed to set page 0 for password unlock check");
148 co_return false;
149 }
150
151 tbuf = buildByteVector(PMBusCmd::statusCML);
152 rbuf.resize(statusByteLength);
153
154 if (!i2cInterface.sendReceive(tbuf, rbuf))
155 {
156 error("Failed to check password unlock status");
157 co_return false;
158 }
159
160 auto unlocked = (rbuf[0] & passwordMatchMask) != 0;
161
162 debug("Password unlock status: {STATUS}", "STATUS",
163 unlocked ? "Unlocked" : "Locked");
164
165 co_return unlocked;
166 }
167
unlockWriteProtect()168 sdbusplus::async::task<bool> MP297X::unlockWriteProtect()
169 {
170 constexpr uint8_t writeProtectModeMask = 0x04;
171 constexpr uint8_t unlockMemoryProtect = 0x00;
172 constexpr uint8_t unlockMTPProtect = 0x63;
173
174 std::vector<uint8_t> tbuf;
175 std::vector<uint8_t> rbuf(statusByteLength);
176
177 // Get write protection mode
178 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
179 if (!i2cInterface.sendReceive(tbuf, rbuf))
180 {
181 error("Failed to set page 1 to check write protection mode");
182 co_return false;
183 }
184
185 tbuf = buildByteVector(MP297XCmd::writeProtectMode);
186 if (!i2cInterface.sendReceive(tbuf, rbuf))
187 {
188 error("Failed to get write protect mode");
189 co_return false;
190 }
191
192 auto isMTPMode = (rbuf[0] & writeProtectModeMask) == 0;
193 auto unlockData = isMTPMode ? unlockMTPProtect : unlockMemoryProtect;
194
195 // Unlock write protection
196 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
197 if (!i2cInterface.sendReceive(tbuf, rbuf))
198 {
199 error("Failed to set page 0 to unlock write protection");
200 co_return false;
201 }
202
203 tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData);
204 if (!i2cInterface.sendReceive(tbuf, rbuf))
205 {
206 error("Failed to unlock write protection");
207 co_return false;
208 }
209
210 debug("Unlocked {MODE} write protection", "MODE",
211 isMTPMode ? "MTP" : "memory");
212 co_return true;
213 }
214
programPageRegisters(MPSPage page,const std::map<uint8_t,std::vector<MPSData>> & groupedData)215 sdbusplus::async::task<bool> MP297X::programPageRegisters(
216 MPSPage page, const std::map<uint8_t, std::vector<MPSData>>& groupedData)
217 {
218 auto pageNum = static_cast<uint8_t>(page);
219
220 if (groupedData.find(pageNum) == groupedData.end())
221 {
222 debug("No data found for page {PAGE}", "PAGE", pageNum);
223 co_return true;
224 }
225
226 const auto& data = groupedData.at(pageNum);
227
228 // Page 2 is the multi-config page. Enter page2A to write config value
229 // to MTP space directly during multi-config page programming.
230 if (page == MPSPage::page2)
231 {
232 page = MPSPage::page2A;
233 pageNum = static_cast<uint8_t>(page);
234 }
235
236 std::vector<uint8_t> tbuf;
237 std::vector<uint8_t> rbuf;
238
239 tbuf = buildByteVector(PMBusCmd::page, page);
240 if (!i2cInterface.sendReceive(tbuf, rbuf))
241 {
242 error("Failed to set page {PAGE} to program registers", "PAGE",
243 pageNum);
244 co_return false;
245 }
246
247 auto i2cWriteWithRetry =
248 [&](const std::vector<uint8_t>& tbuf) -> sdbusplus::async::task<bool> {
249 std::vector<uint8_t> rbuf;
250 constexpr size_t maxRetries = 3;
251 constexpr auto retryDelay = std::chrono::milliseconds(10);
252
253 for (size_t i = 1; i <= maxRetries; ++i)
254 {
255 if (i2cInterface.sendReceive(tbuf, rbuf))
256 {
257 co_return true;
258 }
259 error("I2C write failed, retry {RETRY}", "RETRY", i);
260 co_await sdbusplus::async::sleep_for(ctx, retryDelay);
261 }
262 co_return false;
263 };
264
265 for (const auto& regData : data)
266 {
267 tbuf = {regData.addr};
268 tbuf.insert(tbuf.end(), regData.data.begin(),
269 regData.data.begin() + regData.length);
270
271 if (!(co_await i2cWriteWithRetry(tbuf)))
272 {
273 error(
274 "Failed to write data {DATA} to register {REG} on page {PAGE}",
275 "DATA", lg2::hex, bytesToInt<uint32_t>(regData.data), "REG",
276 lg2::hex, regData.addr, "PAGE", pageNum);
277 co_return false;
278 }
279
280 if (page == MPSPage::page2A)
281 {
282 // Page 2A requires a delay after each register write
283 co_await sdbusplus::async::sleep_for(ctx,
284 std::chrono::milliseconds(2));
285 }
286 }
287
288 debug("Programmed {N} registers on page {PAGE}", "N", data.size(), "PAGE",
289 pageNum);
290
291 co_return true;
292 }
293
storeDataIntoMTP()294 sdbusplus::async::task<bool> MP297X::storeDataIntoMTP()
295 {
296 std::vector<uint8_t> tbuf;
297 std::vector<uint8_t> rbuf;
298
299 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
300 if (!i2cInterface.sendReceive(tbuf, rbuf))
301 {
302 error("Failed to set page 0 for storing data into MTP");
303 co_return false;
304 }
305
306 tbuf = buildByteVector(MP297XCmd::storeDataIntoMTP);
307 if (!i2cInterface.sendReceive(tbuf, rbuf))
308 {
309 error("Failed to store data into MTP");
310 co_return false;
311 }
312
313 // Wait store data into MTP
314 co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
315
316 debug("Stored data into MTP");
317
318 co_return true;
319 }
320
enableMTPPageWriteRead()321 sdbusplus::async::task<bool> MP297X::enableMTPPageWriteRead()
322 {
323 constexpr uint8_t mtpByteRWEnable = 0x20;
324
325 std::vector<uint8_t> tbuf;
326 std::vector<uint8_t> rbuf;
327
328 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
329 if (!i2cInterface.sendReceive(tbuf, rbuf))
330 {
331 error("Failed to set page 1 to enable MTP page write/read");
332 co_return false;
333 }
334
335 tbuf = buildByteVector(MP297XCmd::enableMTPPageWR);
336 rbuf.resize(statusByteLength);
337 if (!i2cInterface.sendReceive(tbuf, rbuf))
338 {
339 error("Failed to read MTP page write/read status");
340 co_return false;
341 }
342
343 uint8_t enableMTPPageWRData = rbuf[0] | mtpByteRWEnable;
344 tbuf = buildByteVector(MP297XCmd::enableMTPPageWR, enableMTPPageWRData);
345 rbuf.resize(0);
346 if (!i2cInterface.sendReceive(tbuf, rbuf))
347 {
348 error("Failed to enable MTP page write/read");
349 co_return false;
350 }
351
352 debug("Enabled MTP page write/read");
353 co_return true;
354 }
355
enableMultiConfigCRC()356 sdbusplus::async::task<bool> MP297X::enableMultiConfigCRC()
357 {
358 std::vector<uint8_t> tbuf;
359 std::vector<uint8_t> rbuf;
360
361 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
362 if (!i2cInterface.sendReceive(tbuf, rbuf))
363 {
364 error("Failed to set page 2 to enable multi-config CRC");
365 co_return false;
366 }
367
368 tbuf = buildByteVector(MP297XCmd::enableMultiConfigCRC);
369 if (!i2cInterface.sendReceive(tbuf, rbuf))
370 {
371 error("Failed to enable multi-config CRC");
372 co_return false;
373 }
374
375 debug("Enabled multi-config CRC");
376 co_return true;
377 }
378
getCRC(uint32_t * checksum)379 sdbusplus::async::task<bool> MP297X::getCRC(uint32_t* checksum)
380 {
381 if (checksum == nullptr)
382 {
383 error("getCRC() called with null checksum pointer");
384 co_return false;
385 }
386
387 constexpr size_t crcLength = 2;
388
389 std::vector<uint8_t> tbuf;
390 std::vector<uint8_t> rbuf;
391
392 uint16_t userCodeCRC = 0;
393 uint16_t multiConfigCRC = 0;
394
395 // Read User Code CRC
396 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page29);
397 rbuf.resize(0);
398 if (!i2cInterface.sendReceive(tbuf, rbuf))
399 {
400 error("Failed to set page 29 for User Code CRC read");
401 co_return false;
402 }
403
404 tbuf = buildByteVector(MP297XCmd::readUserCodeCRC);
405 rbuf.resize(crcLength);
406 if (!i2cInterface.sendReceive(tbuf, rbuf))
407 {
408 error("Failed to read User Code CRC from device");
409 co_return false;
410 }
411
412 userCodeCRC = bytesToInt<uint16_t>(rbuf);
413
414 // Read Multi Config CRC
415 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2A);
416 rbuf.resize(0);
417 if (!i2cInterface.sendReceive(tbuf, rbuf))
418 {
419 error("Failed to set page 2A for Multi Config CRC read");
420 co_return false;
421 }
422
423 tbuf = buildByteVector(MP297XCmd::readMultiConfigCRC);
424 rbuf.resize(crcLength);
425 if (!i2cInterface.sendReceive(tbuf, rbuf))
426 {
427 error("Failed to read Multi Config CRC from device");
428 co_return false;
429 }
430
431 multiConfigCRC = bytesToInt<uint16_t>(rbuf);
432
433 // Combine: [byte3 byte2] = userCodeCRC, [byte1 byte0] = multiConfigCRC
434 *checksum = (static_cast<uint32_t>(userCodeCRC) << 16) |
435 static_cast<uint32_t>(multiConfigCRC);
436
437 debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
438
439 co_return true;
440 }
441
checkMTPCRC()442 sdbusplus::async::task<bool> MP297X::checkMTPCRC()
443 {
444 uint32_t crc = 0;
445 auto expectedCRC = (configuration->crcUser << 16) | configuration->crcMulti;
446
447 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
448 if (!co_await getCRC(&crc))
449 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
450 {
451 error("Failed to get CRC for MTP check");
452 co_return false;
453 }
454
455 debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
456 lg2::hex, expectedCRC);
457
458 co_return crc == expectedCRC;
459 }
460
forcedUpdateAllowed()461 bool MP297X::forcedUpdateAllowed()
462 {
463 return true;
464 }
465
updateFirmware(bool force)466 sdbusplus::async::task<bool> MP297X::updateFirmware(bool force)
467 {
468 (void)force;
469
470 auto groupedConfigData = getGroupedConfigData();
471
472 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId))
473 {
474 co_return false;
475 }
476
477 if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId))
478 {
479 co_return false;
480 }
481
482 if (!co_await isPasswordUnlock())
483 {
484 co_return false;
485 }
486
487 if (!co_await unlockWriteProtect())
488 {
489 co_return false;
490 }
491
492 if (!co_await programPageRegisters(MPSPage::page0, groupedConfigData))
493 {
494 co_return false;
495 }
496
497 if (!co_await programPageRegisters(MPSPage::page1, groupedConfigData))
498 {
499 co_return false;
500 }
501
502 if (!co_await storeDataIntoMTP())
503 {
504 co_return false;
505 }
506
507 if (!co_await enableMTPPageWriteRead())
508 {
509 co_return false;
510 }
511
512 if (!co_await enableMultiConfigCRC())
513 {
514 co_return false;
515 }
516
517 if (!co_await programPageRegisters(MPSPage::page2, groupedConfigData))
518 {
519 co_return false;
520 }
521
522 if (!(co_await checkMTPCRC()))
523 {
524 co_return false;
525 }
526
527 co_return true;
528 }
529
530 } // namespace phosphor::software::VR
531