blob: 634200147feab880aa841e07832940b4ec6472a8 [file] [log] [blame]
Kevin Tung3638c242025-10-07 13:48:13 +08001#include "mp994x.hpp"
2
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
18enum 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
38sdbusplus::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
78sdbusplus::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
104sdbusplus::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
163sdbusplus::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
188sdbusplus::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
244sdbusplus::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
286sdbusplus::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
327sdbusplus::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
358sdbusplus::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
384sdbusplus::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
414sdbusplus::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
469sdbusplus::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
486bool MP994X::forcedUpdateAllowed()
487{
488 return true;
489}
490
491sdbusplus::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