1 #include "mp5998.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 std::string_view crcUserRegName = "CRC_USER";
15 static constexpr uint8_t eepromFaultBit = 0x01;
16 static constexpr uint8_t unlockData = 0x00;
17 static constexpr size_t statusByteLength = 1;
18
19 enum class MP5998Cmd : uint8_t
20 {
21 crcUser = 0xF8,
22 passwordReg = 0x0E,
23 };
24
parseDeviceConfiguration()25 sdbusplus::async::task<bool> MP5998::parseDeviceConfiguration()
26 {
27 if (!configuration)
28 {
29 error("Device configuration not initialized");
30 co_return false;
31 }
32
33 configuration->vendorId = 0x4D5053;
34 configuration->productId = 0x35393938;
35
36 for (const auto& tokens : parser->lineTokens)
37 {
38 if (!parser->isValidDataTokens(tokens))
39 {
40 continue;
41 }
42
43 auto regName = parser->getVal<std::string>(tokens, ATE::regName);
44
45 if (regName == crcUserRegName)
46 {
47 configuration->configId =
48 parser->getVal<uint32_t>(tokens, ATE::configId);
49 configuration->crcUser =
50 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
51 }
52 }
53
54 co_return true;
55 }
56
verifyImage(const uint8_t * image,size_t imageSize)57 sdbusplus::async::task<bool> MP5998::verifyImage(const uint8_t* image,
58 size_t imageSize)
59 {
60 if (!co_await parseImage(image, imageSize))
61 {
62 error("Image verification failed: image parsing failed");
63 co_return false;
64 }
65
66 if (configuration->registersData.empty())
67 {
68 error("Image verification failed - no register data found");
69 co_return false;
70 }
71
72 if (configuration->configId == 0)
73 {
74 error("Image verification failed - missing config ID");
75 co_return false;
76 }
77
78 co_return true;
79 }
80
checkId(PMBusCmd idCmd,uint32_t expected)81 sdbusplus::async::task<bool> MP5998::checkId(PMBusCmd idCmd, uint32_t expected)
82 {
83 static constexpr size_t mfrIdLength = 3;
84 static constexpr size_t mfrModelLength = 5;
85
86 std::vector<uint8_t> tbuf;
87 std::vector<uint8_t> rbuf;
88
89 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
90 if (!i2cInterface.sendReceive(tbuf, rbuf))
91 {
92 error("MP5998: Failed to set page 0 for ID check");
93 co_return false;
94 }
95
96 size_t bufferSize;
97
98 if (idCmd == PMBusCmd::mfrId)
99 {
100 bufferSize = statusByteLength + mfrIdLength;
101 }
102 else if (idCmd == PMBusCmd::mfrModel)
103 {
104 bufferSize = statusByteLength + mfrModelLength;
105 }
106 else
107 {
108 error("MP5998: Unsupported ID command: 0x{CMD}", "CMD", lg2::hex,
109 static_cast<uint8_t>(idCmd));
110 co_return false;
111 }
112
113 tbuf = buildByteVector(idCmd);
114 rbuf.resize(bufferSize);
115
116 if (!i2cInterface.sendReceive(tbuf, rbuf))
117 {
118 error("MP5998: I2C sendReceive failed for command 0x{CMD}", "CMD",
119 lg2::hex, static_cast<uint8_t>(idCmd));
120 co_return false;
121 }
122
123 auto idBytes = std::span(rbuf).subspan(statusByteLength);
124 uint32_t id;
125 if (idCmd == PMBusCmd::mfrModel)
126 {
127 auto productBytes = idBytes.subspan(1, 4);
128 id = bytesToInt<uint32_t>(productBytes);
129 }
130 else
131 {
132 id = bytesToInt<uint32_t>(idBytes);
133 }
134
135 debug("Check ID cmd {CMD}: Got={ID}, Expected={EXP}", "CMD", lg2::hex,
136 static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP", lg2::hex,
137 expected);
138
139 co_return id == expected;
140 }
141
unlockPasswordProtection()142 sdbusplus::async::task<bool> MP5998::unlockPasswordProtection()
143 {
144 constexpr uint8_t passwordUnlockBit = 0x08;
145 constexpr uint16_t passwordData = 0x0000;
146
147 std::vector<uint8_t> tbuf;
148 std::vector<uint8_t> rbuf;
149
150 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
151 if (!i2cInterface.sendReceive(tbuf, rbuf))
152 {
153 error("Failed to set page 0 for password unlock");
154 co_return false;
155 }
156
157 tbuf = buildByteVector(MP5998Cmd::passwordReg, passwordData);
158 if (!i2cInterface.sendReceive(tbuf, rbuf))
159 {
160 error("Failed to write password");
161 co_return false;
162 }
163
164 tbuf = buildByteVector(PMBusCmd::statusCML);
165 rbuf.resize(statusByteLength);
166 if (!i2cInterface.sendReceive(tbuf, rbuf))
167 {
168 error("Failed to read STATUS_CML");
169 co_return false;
170 }
171
172 bool unlocked = (rbuf[0] & passwordUnlockBit) == 0;
173
174 co_return unlocked;
175 }
176
unlockWriteProtection()177 sdbusplus::async::task<bool> MP5998::unlockWriteProtection()
178 {
179 std::vector<uint8_t> tbuf;
180 std::vector<uint8_t> rbuf;
181
182 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
183 if (!i2cInterface.sendReceive(tbuf, rbuf))
184 {
185 error("Failed to set page 0 for write protection unlock");
186 co_return false;
187 }
188
189 tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData);
190 if (!i2cInterface.sendReceive(tbuf, rbuf))
191 {
192 error("Failed to unlock write protection");
193 co_return false;
194 }
195
196 debug("Write protection unlocked");
197 co_return true;
198 }
199
programAllRegisters()200 sdbusplus::async::task<bool> MP5998::programAllRegisters()
201 {
202 uint8_t currentPage = 0xFF;
203
204 for (const auto& regData : configuration->registersData)
205 {
206 if (regData.page != currentPage)
207 {
208 std::vector<uint8_t> tbuf =
209 buildByteVector(PMBusCmd::page, regData.page);
210 std::vector<uint8_t> rbuf;
211 if (!i2cInterface.sendReceive(tbuf, rbuf))
212 {
213 error("Failed to set page {PAGE}", "PAGE", regData.page);
214 co_return false;
215 }
216 currentPage = regData.page;
217 }
218
219 std::vector<uint8_t> tbuf;
220 std::vector<uint8_t> rbuf;
221
222 tbuf.push_back(regData.addr);
223
224 for (uint8_t i = 0; i < regData.length && i < 4; ++i)
225 {
226 tbuf.push_back(regData.data[i]);
227 }
228
229 if (!i2cInterface.sendReceive(tbuf, rbuf))
230 {
231 error("Failed to write register 0x{REG} on page {PAGE}", "REG",
232 lg2::hex, regData.addr, "PAGE", regData.page);
233 co_return false;
234 }
235 }
236
237 debug("All registers programmed successfully");
238 co_return true;
239 }
240
storeMTP()241 sdbusplus::async::task<bool> MP5998::storeMTP()
242 {
243 std::vector<uint8_t> tbuf;
244 std::vector<uint8_t> rbuf;
245
246 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
247 if (!i2cInterface.sendReceive(tbuf, rbuf))
248 {
249 error("Failed to set page 0 for MTP store");
250 co_return false;
251 }
252
253 tbuf = buildByteVector(PMBusCmd::storeUserCode);
254 if (!i2cInterface.sendReceive(tbuf, rbuf))
255 {
256 error("Failed to send STORE_USER_ALL command");
257 co_return false;
258 }
259
260 co_return true;
261 }
262
waitForMTPComplete()263 sdbusplus::async::task<bool> MP5998::waitForMTPComplete()
264 {
265 constexpr uint16_t mtpStoreWaitmS = 1200;
266 co_await sdbusplus::async::sleep_for(
267 ctx, std::chrono::milliseconds(mtpStoreWaitmS));
268 std::vector<uint8_t> tbuf = buildByteVector(PMBusCmd::statusCML);
269 std::vector<uint8_t> rbuf;
270 rbuf.resize(statusByteLength);
271
272 if (!i2cInterface.sendReceive(tbuf, rbuf))
273 {
274 error("Failed to read STATUS_CML after MTP store");
275 co_return false;
276 }
277
278 bool eepromFault = rbuf[0] & eepromFaultBit;
279
280 if (eepromFault)
281 {
282 error("EEPROM fault detected after MTP store");
283 co_return false;
284 }
285
286 co_return true;
287 }
288
verifyCRC()289 sdbusplus::async::task<bool> MP5998::verifyCRC()
290 {
291 uint32_t deviceCRC{0};
292 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
293 bool getCRCSuccess = co_await getCRC(&deviceCRC);
294 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
295 if (!getCRCSuccess)
296 {
297 error("Failed to read CRC from device");
298 co_return false;
299 }
300
301 bool crcMatch = (deviceCRC == configuration->crcUser);
302
303 co_return crcMatch;
304 }
305
getCRC(uint32_t * checksum)306 sdbusplus::async::task<bool> MP5998::getCRC(uint32_t* checksum)
307 {
308 constexpr size_t crcLength = 2;
309
310 std::vector<uint8_t> tbuf;
311 std::vector<uint8_t> rbuf;
312
313 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
314 if (!i2cInterface.sendReceive(tbuf, rbuf))
315 {
316 error("Failed to set page 0 for CRC read");
317 co_return false;
318 }
319
320 tbuf = buildByteVector(MP5998Cmd::crcUser);
321 rbuf.resize(crcLength);
322 if (!i2cInterface.sendReceive(tbuf, rbuf))
323 {
324 error("Failed to read CRC_USER register");
325 co_return false;
326 }
327
328 *checksum = bytesToInt<uint32_t>(rbuf);
329
330 co_return true;
331 }
332
sendRestoreMTPCommand()333 sdbusplus::async::task<bool> MP5998::sendRestoreMTPCommand()
334 {
335 std::vector<uint8_t> tbuf;
336 std::vector<uint8_t> rbuf;
337
338 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
339 if (!i2cInterface.sendReceive(tbuf, rbuf))
340 {
341 error("Failed to set page 0 for MTP restore");
342 co_return false;
343 }
344
345 tbuf = buildByteVector(PMBusCmd::restoreUserAll);
346 if (!i2cInterface.sendReceive(tbuf, rbuf))
347 {
348 error("Failed to send RESTORE_ALL command");
349 co_return false;
350 }
351
352 co_return true;
353 }
354
checkEEPROMFaultAfterRestore()355 sdbusplus::async::task<bool> MP5998::checkEEPROMFaultAfterRestore()
356 {
357 std::vector<uint8_t> tbuf;
358 std::vector<uint8_t> rbuf;
359
360 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
361 if (!i2cInterface.sendReceive(tbuf, rbuf))
362 {
363 error("Failed to set page 0 for EEPROM fault check");
364 co_return false;
365 }
366
367 tbuf = buildByteVector(PMBusCmd::statusCML);
368 rbuf.resize(1);
369 if (!i2cInterface.sendReceive(tbuf, rbuf))
370 {
371 error("Failed to read STATUS_CML register");
372 co_return false;
373 }
374
375 bool eepromFault = (rbuf[0] & eepromFaultBit) != 0;
376
377 co_return !eepromFault;
378 }
379
restoreMTPAndVerify()380 sdbusplus::async::task<bool> MP5998::restoreMTPAndVerify()
381 {
382 constexpr uint16_t mtpRestoreWait = 1600;
383
384 if (!co_await sendRestoreMTPCommand())
385 {
386 error("Failed to send RESTORE_ALL command");
387 co_return false;
388 }
389
390 co_await sdbusplus::async::sleep_for(
391 ctx, std::chrono::microseconds(mtpRestoreWait));
392 if (!co_await checkEEPROMFaultAfterRestore())
393 {
394 error("EEPROM fault detected after MTP restore");
395 co_return false;
396 }
397
398 co_return true;
399 }
400
forcedUpdateAllowed()401 bool MP5998::forcedUpdateAllowed()
402 {
403 return true;
404 }
405
updateFirmware(bool force)406 sdbusplus::async::task<bool> MP5998::updateFirmware(bool force)
407 {
408 (void)force;
409
410 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId))
411 {
412 co_return false;
413 }
414
415 if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId))
416 {
417 co_return false;
418 }
419
420 if (!co_await unlockWriteProtection())
421 {
422 co_return false;
423 }
424
425 if (!co_await programAllRegisters())
426 {
427 co_return false;
428 }
429
430 if (!co_await storeMTP())
431 {
432 co_return false;
433 }
434
435 if (!co_await waitForMTPComplete())
436 {
437 co_return false;
438 }
439
440 if (!co_await verifyCRC())
441 {
442 co_return false;
443 }
444
445 if (!co_await restoreMTPAndVerify())
446 {
447 co_return false;
448 }
449
450 co_return true;
451 }
452
453 } // namespace phosphor::software::VR
454