blob: 2dabada12df84891f09c0a6e69b64c42235b7424 [file] [log] [blame]
Kevin Tungf7309732025-11-12 15:27:53 +08001#include "mpx9xx.hpp"
Kevin Tung3638c242025-10-07 13:48:13 +08002
3#include "common/include/utils.hpp"
4
5#include <phosphor-logging/lg2.hpp>
6
7PHOSPHOR_LOG2_USING;
8
9namespace phosphor::software::VR
10{
11
12static constexpr std::string_view vendorIdRegName = "VENDOR_ID_VR";
13static constexpr std::string_view mfrDeviceIDCFGRegName = "MFR_DEVICE_ID_CFG";
14static constexpr std::string_view crcUserMultiRegName = "CRC_USER_MULTI";
15
16static constexpr uint8_t pageMask = 0x0F;
17
Kevin Tungf7309732025-11-12 15:27:53 +080018enum class MPX9XXCmd : uint8_t
Kevin Tung3638c242025-10-07 13:48:13 +080019{
20 // Page 0 commands
21 storeUserAll = 0x15,
22 userData08 = 0xB8,
23
24 // Page 2 commands
Kevin Tungf7309732025-11-12 15:27:53 +080025 configIdMP292X = 0xA9,
Kevin Tung3638c242025-10-07 13:48:13 +080026 mfrMulconfigSel = 0xAB,
Kevin Tungf7309732025-11-12 15:27:53 +080027 configIdMP994X = 0xAF,
Kevin Tung3638c242025-10-07 13:48:13 +080028 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
Kevin Tungf7309732025-11-12 15:27:53 +080039MPX9XXCmd MP292X::getConfigIdCmd() const
40{
41 return MPX9XXCmd::configIdMP292X;
42}
43
44MPX9XXCmd MP994X::getConfigIdCmd() const
45{
46 return MPX9XXCmd::configIdMP994X;
47}
48
49sdbusplus::async::task<bool> MPX9XX::parseDeviceConfiguration()
Kevin Tung3638c242025-10-07 13:48:13 +080050{
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
Kevin Tungf7309732025-11-12 15:27:53 +080089sdbusplus::async::task<bool> MPX9XX::verifyImage(const uint8_t* image,
Kevin Tung3638c242025-10-07 13:48:13 +080090 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
Kevin Tungf7309732025-11-12 15:27:53 +0800115sdbusplus::async::task<bool> MPX9XX::checkId(MPX9XXCmd idCmd, uint32_t expected)
Kevin Tung3638c242025-10-07 13:48:13 +0800116{
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 {
Kevin Tungf7309732025-11-12 15:27:53 +0800127 case MPX9XXCmd::vendorId:
Kevin Tung3638c242025-10-07 13:48:13 +0800128 page = MPSPage::page5;
129 idLen = vendorIdLength;
130 break;
Kevin Tungf7309732025-11-12 15:27:53 +0800131 case MPX9XXCmd::deviceId:
Kevin Tung3638c242025-10-07 13:48:13 +0800132 page = MPSPage::page2;
133 idLen = productIdLength;
134 break;
Kevin Tungf7309732025-11-12 15:27:53 +0800135 case MPX9XXCmd::configIdMP292X:
136 case MPX9XXCmd::configIdMP994X:
Kevin Tung3638c242025-10-07 13:48:13 +0800137 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
Kevin Tungf7309732025-11-12 15:27:53 +0800175sdbusplus::async::task<bool> MPX9XX::unlockWriteProtect()
Kevin Tung3638c242025-10-07 13:48:13 +0800176{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800200sdbusplus::async::task<bool> MPX9XX::disableStoreFaultTriggering()
Kevin Tung3638c242025-10-07 13:48:13 +0800201{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800217 tbuf = buildByteVector(MPX9XXCmd::mfrDebug);
Kevin Tung3638c242025-10-07 13:48:13 +0800218 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;
Kevin Tungf7309732025-11-12 15:27:53 +0800226 tbuf = buildByteVector(MPX9XXCmd::mfrDebug, data);
Kevin Tung3638c242025-10-07 13:48:13 +0800227 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
Kevin Tungf7309732025-11-12 15:27:53 +0800242 tbuf = buildByteVector(MPX9XXCmd::storeFaultTrigger,
Kevin Tung3638c242025-10-07 13:48:13 +0800243 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
Kevin Tungf7309732025-11-12 15:27:53 +0800256sdbusplus::async::task<bool> MPX9XX::setMultiConfigAddress(uint8_t config)
Kevin Tung3638c242025-10-07 13:48:13 +0800257{
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;
Kevin Tungf7309732025-11-12 15:27:53 +0800285 tbuf = buildByteVector(MPX9XXCmd::mfrMulconfigSel, selectAddrData);
Kevin Tung3638c242025-10-07 13:48:13 +0800286 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,
Kevin Tungf7309732025-11-12 15:27:53 +0800290 static_cast<uint8_t>(MPX9XXCmd::mfrMulconfigSel));
Kevin Tung3638c242025-10-07 13:48:13 +0800291 co_return false;
292 }
293
294 debug("Selected multi config set address {ADDR}", "ADDR", addr);
295 co_return true;
296}
297
Kevin Tungf7309732025-11-12 15:27:53 +0800298sdbusplus::async::task<bool> MPX9XX::programConfigData(
Kevin Tung3638c242025-10-07 13:48:13 +0800299 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
Kevin Tungf7309732025-11-12 15:27:53 +0800339sdbusplus::async::task<bool> MPX9XX::programAllRegisters()
Kevin Tung3638c242025-10-07 13:48:13 +0800340{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800370sdbusplus::async::task<bool> MPX9XX::storeDataIntoMTP()
Kevin Tung3638c242025-10-07 13:48:13 +0800371{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800382 tbuf = buildByteVector(MPX9XXCmd::storeUserAll);
Kevin Tung3638c242025-10-07 13:48:13 +0800383 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
Kevin Tungf7309732025-11-12 15:27:53 +0800396sdbusplus::async::task<bool> MPX9XX::getCRC(uint32_t* checksum)
Kevin Tung3638c242025-10-07 13:48:13 +0800397{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800411 tbuf = buildByteVector(MPX9XXCmd::userData08);
Kevin Tung3638c242025-10-07 13:48:13 +0800412 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
Kevin Tungf7309732025-11-12 15:27:53 +0800426sdbusplus::async::task<bool> MPX9XX::restoreDataFromNVM()
Kevin Tung3638c242025-10-07 13:48:13 +0800427{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800442 tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl);
Kevin Tung3638c242025-10-07 13:48:13 +0800443 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;
Kevin Tungf7309732025-11-12 15:27:53 +0800451 tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl, data);
Kevin Tung3638c242025-10-07 13:48:13 +0800452 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
Kevin Tungf7309732025-11-12 15:27:53 +0800481sdbusplus::async::task<bool> MPX9XX::checkMTPCRC()
Kevin Tung3638c242025-10-07 13:48:13 +0800482{
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
Kevin Tungf7309732025-11-12 15:27:53 +0800498bool MPX9XX::forcedUpdateAllowed()
Kevin Tung3638c242025-10-07 13:48:13 +0800499{
500 return true;
501}
502
Kevin Tungf7309732025-11-12 15:27:53 +0800503sdbusplus::async::task<bool> MPX9XX::updateFirmware(bool force)
Kevin Tung3638c242025-10-07 13:48:13 +0800504{
505 (void)force;
506
507 if (!configuration)
508 {
509 error("Configuration not loaded");
510 co_return false;
511 }
512
Kevin Tungf7309732025-11-12 15:27:53 +0800513 if (!co_await checkId(MPX9XXCmd::vendorId, configuration->vendorId))
Kevin Tung3638c242025-10-07 13:48:13 +0800514 {
515 co_return false;
516 }
517
Kevin Tungf7309732025-11-12 15:27:53 +0800518 if (!co_await checkId(MPX9XXCmd::deviceId, configuration->productId))
Kevin Tung3638c242025-10-07 13:48:13 +0800519 {
520 co_return false;
521 }
522
Kevin Tungf7309732025-11-12 15:27:53 +0800523 if (!co_await checkId(getConfigIdCmd(), configuration->configId))
Kevin Tung3638c242025-10-07 13:48:13 +0800524 {
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