xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mpx9xx.cpp (revision f730973b554c83b7a8e85232e9c730a89d5f3692)
1 #include "mpx9xx.hpp"
2 
3 #include "common/include/utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 PHOSPHOR_LOG2_USING;
8 
9 namespace phosphor::software::VR
10 {
11 
12 static constexpr std::string_view vendorIdRegName = "VENDOR_ID_VR";
13 static constexpr std::string_view mfrDeviceIDCFGRegName = "MFR_DEVICE_ID_CFG";
14 static constexpr std::string_view crcUserMultiRegName = "CRC_USER_MULTI";
15 
16 static constexpr uint8_t pageMask = 0x0F;
17 
18 enum class MPX9XXCmd : uint8_t
19 {
20     // Page 0 commands
21     storeUserAll = 0x15,
22     userData08 = 0xB8,
23 
24     // Page 2 commands
25     configIdMP292X = 0xA9,
26     mfrMulconfigSel = 0xAB,
27     configIdMP994X = 0xAF,
28     mfrNVMPmbusCtrl = 0xCA,
29     mfrDebug = 0xD4,
30     deviceId = 0xDB,
31 
32     // Page 5 commands
33     vendorId = 0xBA,
34 
35     // Page 7 commands
36     storeFaultTrigger = 0x51,
37 };
38 
getConfigIdCmd() const39 MPX9XXCmd MP292X::getConfigIdCmd() const
40 {
41     return MPX9XXCmd::configIdMP292X;
42 }
43 
getConfigIdCmd() const44 MPX9XXCmd MP994X::getConfigIdCmd() const
45 {
46     return MPX9XXCmd::configIdMP994X;
47 }
48 
parseDeviceConfiguration()49 sdbusplus::async::task<bool> MPX9XX::parseDeviceConfiguration()
50 {
51     if (!configuration)
52     {
53         error("Device configuration not initialized");
54         co_return false;
55     }
56 
57     for (const auto& tokens : parser->lineTokens)
58     {
59         if (!parser->isValidDataTokens(tokens))
60         {
61             continue;
62         }
63 
64         auto regName = parser->getVal<std::string>(tokens, ATE::regName);
65 
66         if (regName == vendorIdRegName)
67         {
68             configuration->vendorId =
69                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
70             configuration->configId =
71                 parser->getVal<uint32_t>(tokens, ATE::configId);
72         }
73         else if (regName == mfrDeviceIDCFGRegName)
74         {
75             configuration->productId =
76                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
77         }
78         else if (regName == crcUserMultiRegName)
79         {
80             configuration->crcMulti =
81                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
82             break;
83         }
84     }
85 
86     co_return true;
87 }
88 
verifyImage(const uint8_t * image,size_t imageSize)89 sdbusplus::async::task<bool> MPX9XX::verifyImage(const uint8_t* image,
90                                                  size_t imageSize)
91 {
92     if (!co_await parseImage(image, imageSize, MPSImageType::type1))
93     {
94         error("Image verification failed: image parsing failed");
95         co_return false;
96     }
97 
98     if (configuration->registersData.empty())
99     {
100         error("Image verification failed: no data found");
101         co_return false;
102     }
103 
104     if (configuration->vendorId == 0 || configuration->productId == 0 ||
105         configuration->configId == 0)
106     {
107         error("Image verification failed: missing required field "
108               "vendor ID, product ID, or config ID");
109         co_return false;
110     }
111 
112     co_return true;
113 }
114 
checkId(MPX9XXCmd idCmd,uint32_t expected)115 sdbusplus::async::task<bool> MPX9XX::checkId(MPX9XXCmd idCmd, uint32_t expected)
116 {
117     static constexpr size_t vendorIdLength = 2;
118     static constexpr size_t productIdLength = 1;
119     static constexpr size_t configIdLength = 2;
120 
121     MPSPage page;
122     size_t idLen = 0;
123     const uint8_t cmd = static_cast<uint8_t>(idCmd);
124 
125     switch (idCmd)
126     {
127         case MPX9XXCmd::vendorId:
128             page = MPSPage::page5;
129             idLen = vendorIdLength;
130             break;
131         case MPX9XXCmd::deviceId:
132             page = MPSPage::page2;
133             idLen = productIdLength;
134             break;
135         case MPX9XXCmd::configIdMP292X:
136         case MPX9XXCmd::configIdMP994X:
137             page = MPSPage::page2;
138             idLen = configIdLength;
139             break;
140         default:
141             error("Invalid command for ID check: {CMD}", "CMD", lg2::hex, cmd);
142             co_return false;
143     }
144 
145     std::vector<uint8_t> tbuf;
146     std::vector<uint8_t> rbuf;
147 
148     tbuf = buildByteVector(PMBusCmd::page, page);
149     if (!i2cInterface.sendReceive(tbuf, rbuf))
150     {
151         error("Failed to set page for ID check");
152         co_return false;
153     }
154 
155     tbuf = buildByteVector(idCmd);
156     rbuf.resize(idLen);
157     if (!i2cInterface.sendReceive(tbuf, rbuf))
158     {
159         error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex, cmd);
160         co_return false;
161     }
162 
163     auto id = bytesToInt<uint32_t>(rbuf);
164 
165     if (id != expected)
166     {
167         error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
168               lg2::hex, cmd, "ID", lg2::hex, id, "EXP", lg2::hex, expected);
169         co_return false;
170     }
171 
172     co_return true;
173 }
174 
unlockWriteProtect()175 sdbusplus::async::task<bool> MPX9XX::unlockWriteProtect()
176 {
177     static constexpr uint8_t unlockWriteProtectData = 0x00;
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 to unlock write protection mode");
186         co_return false;
187     }
188 
189     tbuf = buildByteVector(PMBusCmd::writeProtect, unlockWriteProtectData);
190     if (!i2cInterface.sendReceive(tbuf, rbuf))
191     {
192         error("Failed to unlock write protect mode");
193         co_return false;
194     }
195 
196     debug("Write protection unlocked");
197     co_return true;
198 }
199 
disableStoreFaultTriggering()200 sdbusplus::async::task<bool> MPX9XX::disableStoreFaultTriggering()
201 {
202     static constexpr size_t mfrDebugDataLength = 2;
203     static constexpr uint16_t enableEnteringPage7Mask = 0x8000;
204     static constexpr uint16_t disableStoreFaultTriggeringData = 0x1000;
205 
206     std::vector<uint8_t> tbuf;
207     std::vector<uint8_t> rbuf;
208 
209     // enable entering page 7
210     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
211     if (!i2cInterface.sendReceive(tbuf, rbuf))
212     {
213         error("Failed to set page 2 to enable entering page 7");
214         co_return false;
215     }
216 
217     tbuf = buildByteVector(MPX9XXCmd::mfrDebug);
218     rbuf.resize(mfrDebugDataLength);
219     if (!i2cInterface.sendReceive(tbuf, rbuf))
220     {
221         error("Failed to read MFR Debug register to enable entering 7");
222         co_return false;
223     }
224 
225     uint16_t data = (rbuf[1] << 8) | rbuf[0] | enableEnteringPage7Mask;
226     tbuf = buildByteVector(MPX9XXCmd::mfrDebug, data);
227     rbuf.clear();
228     if (!i2cInterface.sendReceive(tbuf, rbuf))
229     {
230         error("Failed to enable entering page 7");
231         co_return false;
232     }
233 
234     // disable store fault triggering
235     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page7);
236     if (!i2cInterface.sendReceive(tbuf, rbuf))
237     {
238         error("Failed to set page 7 to disable store fault triggering");
239         co_return false;
240     }
241 
242     tbuf = buildByteVector(MPX9XXCmd::storeFaultTrigger,
243                            disableStoreFaultTriggeringData);
244     rbuf.clear();
245     if (!i2cInterface.sendReceive(tbuf, rbuf))
246     {
247         error("Failed to disable store fault triggering");
248         co_return false;
249     }
250 
251     debug("Disabled store fault triggerring");
252 
253     co_return true;
254 }
255 
setMultiConfigAddress(uint8_t config)256 sdbusplus::async::task<bool> MPX9XX::setMultiConfigAddress(uint8_t config)
257 {
258     // MPS994X: Select multi-configuration address
259     // Write to Page 2 @ 0xAB:
260     //   - Bit[3] = MFR_MULCONFIG_SEL (1 = enable)
261     //   - Bit[2:0] = MFR_MULCONFIG_ADDR (0 ~ 7 → selects one of 8 configs)
262     // Resulting values for config set 1 ~ 8: 0x08 ~ 0x0F
263 
264     auto addr = config - 1;
265 
266     static constexpr uint8_t maxMultiConfigAddr = 7;
267     static constexpr uint8_t enableMultiConfigAddrSel = 0x08;
268 
269     if (addr > maxMultiConfigAddr)
270     {
271         error("Invalid multi config address: {ADDR}", "ADDR", addr);
272     }
273 
274     std::vector<uint8_t> tbuf;
275     std::vector<uint8_t> rbuf;
276 
277     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
278     if (!i2cInterface.sendReceive(tbuf, rbuf))
279     {
280         error("Failed to set page 2 to set multi config address");
281         co_return false;
282     }
283 
284     uint8_t selectAddrData = enableMultiConfigAddrSel + addr;
285     tbuf = buildByteVector(MPX9XXCmd::mfrMulconfigSel, selectAddrData);
286     if (!i2cInterface.sendReceive(tbuf, rbuf))
287     {
288         error("Failed to write {DATA} to multi config select register {REG}",
289               "DATA", lg2::hex, selectAddrData, "REG", lg2::hex,
290               static_cast<uint8_t>(MPX9XXCmd::mfrMulconfigSel));
291         co_return false;
292     }
293 
294     debug("Selected multi config set address {ADDR}", "ADDR", addr);
295     co_return true;
296 }
297 
programConfigData(const std::vector<MPSData> & gdata)298 sdbusplus::async::task<bool> MPX9XX::programConfigData(
299     const std::vector<MPSData>& gdata)
300 {
301     std::vector<uint8_t> tbuf;
302     std::vector<uint8_t> rbuf;
303 
304     for (const auto& data : gdata)
305     {
306         uint8_t page = data.page & pageMask;
307 
308         tbuf = buildByteVector(PMBusCmd::page, page);
309         if (!i2cInterface.sendReceive(tbuf, rbuf))
310         {
311             error("Failed to set page {PAGE} for register {REG}", "PAGE", page,
312                   "REG", lg2::hex, data.addr);
313             co_return false;
314         }
315 
316         tbuf = {data.addr};
317         tbuf.insert(tbuf.end(), data.data.begin(),
318                     data.data.begin() + data.length);
319 
320         if (!i2cInterface.sendReceive(tbuf, rbuf))
321         {
322             error(
323                 "Failed to write data {DATA} to register {REG} on page {PAGE}",
324                 "DATA", lg2::hex, bytesToInt<uint32_t>(data.data), "REG",
325                 lg2::hex, data.addr, "PAGE", page);
326             co_return false;
327         }
328     }
329 
330     if (!co_await storeDataIntoMTP())
331     {
332         error("Failed to store code into MTP after programming config data");
333         co_return false;
334     }
335 
336     co_return true;
337 }
338 
programAllRegisters()339 sdbusplus::async::task<bool> MPX9XX::programAllRegisters()
340 {
341     // config 0 = User Registers
342     // config 1 ~ 8 = Multi-configuration Registers
343     for (const auto& [config, gdata] : getGroupedConfigData(~pageMask, 4))
344     {
345         debug("Configuring registers for config set {SET}", "SET", config);
346 
347         if (config > 0)
348         {
349             if (!co_await setMultiConfigAddress(config))
350             {
351                 co_return false;
352             }
353         }
354 
355         if (!co_await programConfigData(gdata))
356         {
357             error("Failed to program config set {SET}", "SET", config);
358             co_return false;
359         }
360 
361         debug("Configured {SIZE} registers for config set {SET}", "SIZE",
362               gdata.size(), "SET", config);
363     }
364 
365     debug("All registers were programmed successfully");
366 
367     co_return true;
368 }
369 
storeDataIntoMTP()370 sdbusplus::async::task<bool> MPX9XX::storeDataIntoMTP()
371 {
372     std::vector<uint8_t> tbuf;
373     std::vector<uint8_t> rbuf;
374 
375     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
376     if (!i2cInterface.sendReceive(tbuf, rbuf))
377     {
378         error("Failed to set page 0 to store data into MTP");
379         co_return false;
380     }
381 
382     tbuf = buildByteVector(MPX9XXCmd::storeUserAll);
383     if (!i2cInterface.sendReceive(tbuf, rbuf))
384     {
385         error("Failed to store data into MTP");
386         co_return false;
387     }
388 
389     // Wait store data
390     co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(1000));
391 
392     debug("Stored data into MTP");
393     co_return true;
394 }
395 
getCRC(uint32_t * checksum)396 sdbusplus::async::task<bool> MPX9XX::getCRC(uint32_t* checksum)
397 {
398     static constexpr size_t crcUserMultiDataLength = 4;
399     static constexpr size_t statusByteLength = 1;
400 
401     std::vector<uint8_t> tbuf;
402     std::vector<uint8_t> rbuf;
403 
404     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
405     if (!i2cInterface.sendReceive(tbuf, rbuf))
406     {
407         error("Failed to set page 0 for get user data");
408         co_return false;
409     }
410 
411     tbuf = buildByteVector(MPX9XXCmd::userData08);
412     rbuf.resize(crcUserMultiDataLength + statusByteLength);
413     if (!i2cInterface.sendReceive(tbuf, rbuf))
414     {
415         error("Failed to get user data on page 0");
416         co_return false;
417     }
418 
419     auto crcBytes = std::span(rbuf).subspan(statusByteLength);
420     *checksum = bytesToInt<uint32_t>(crcBytes);
421 
422     debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
423     co_return true;
424 }
425 
restoreDataFromNVM()426 sdbusplus::async::task<bool> MPX9XX::restoreDataFromNVM()
427 {
428     static constexpr size_t nvmPmbusCtrlDataLength = 2;
429     static constexpr uint16_t enableRestoreDataFromMTPMask = 0x0008;
430 
431     std::vector<uint8_t> tbuf;
432     std::vector<uint8_t> rbuf;
433 
434     // enable restore data from MTP
435     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
436     if (!i2cInterface.sendReceive(tbuf, rbuf))
437     {
438         error("Failed to set page 2 to enable restore data from MTP");
439         co_return false;
440     }
441 
442     tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl);
443     rbuf.resize(nvmPmbusCtrlDataLength);
444     if (!i2cInterface.sendReceive(tbuf, rbuf))
445     {
446         error("Failed to read NVM PMBUS Ctrl register");
447         co_return false;
448     }
449 
450     uint16_t data = ((rbuf[1] << 8) | rbuf[0]) | enableRestoreDataFromMTPMask;
451     tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl, data);
452     rbuf.clear();
453     if (!i2cInterface.sendReceive(tbuf, rbuf))
454     {
455         error("Failed to enable restore data from MTP");
456         co_return false;
457     }
458 
459     // restore data from NVM
460     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
461     if (!i2cInterface.sendReceive(tbuf, rbuf))
462     {
463         error("Failed to set page 0 for restore MTP and verify");
464     }
465 
466     tbuf = buildByteVector(PMBusCmd::restoreUserAll);
467     if (!i2cInterface.sendReceive(tbuf, rbuf))
468     {
469         error("Failed to restore data from NVM");
470         co_return false;
471     }
472 
473     // wait restore data
474     co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
475 
476     debug("Restored data from NVM success");
477 
478     co_return true;
479 }
480 
checkMTPCRC()481 sdbusplus::async::task<bool> MPX9XX::checkMTPCRC()
482 {
483     uint32_t crc = 0;
484     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
485     if (!co_await getCRC(&crc))
486     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
487     {
488         error("Failed to get CRC for MTP check");
489         co_return false;
490     }
491 
492     debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
493           lg2::hex, configuration->crcMulti);
494 
495     co_return configuration->crcMulti == crc;
496 }
497 
forcedUpdateAllowed()498 bool MPX9XX::forcedUpdateAllowed()
499 {
500     return true;
501 }
502 
updateFirmware(bool force)503 sdbusplus::async::task<bool> MPX9XX::updateFirmware(bool force)
504 {
505     (void)force;
506 
507     if (!configuration)
508     {
509         error("Configuration not loaded");
510         co_return false;
511     }
512 
513     if (!co_await checkId(MPX9XXCmd::vendorId, configuration->vendorId))
514     {
515         co_return false;
516     }
517 
518     if (!co_await checkId(MPX9XXCmd::deviceId, configuration->productId))
519     {
520         co_return false;
521     }
522 
523     if (!co_await checkId(getConfigIdCmd(), configuration->configId))
524     {
525         co_return false;
526     }
527 
528     if (!co_await unlockWriteProtect())
529     {
530         co_return false;
531     }
532 
533     if (!co_await disableStoreFaultTriggering())
534     {
535         co_return false;
536     }
537 
538     if (!co_await programAllRegisters())
539     {
540         co_return false;
541     }
542 
543     if (!co_await restoreDataFromNVM())
544     {
545         co_return false;
546     }
547 
548     if (!co_await checkMTPCRC())
549     {
550         co_return false;
551     }
552 
553     co_return true;
554 }
555 
556 } // namespace phosphor::software::VR
557