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