xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mp5998.cpp (revision 782d6eed913236bc78c533551876389f176bf121)
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