1 #include "mp2x6xx.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 size_t vendorIdLength = 3;
13 static constexpr size_t deviceIdLength = 4;
14 static constexpr size_t configIdLength = 2;
15 static constexpr size_t statusByteLength = 1;
16 static constexpr size_t crcLength = 2;
17
18 static constexpr std::string_view productIdRegName = "TRIM_MFR_PRODUCT_ID2";
19 static constexpr std::string_view crcUserRegName = "CRC_USER";
20
21 static constexpr uint8_t pageMask = 0x0F;
22 static constexpr uint8_t configMask = 0xF0;
23
24 static constexpr uint8_t disableWriteProtect = 0x00;
25 static constexpr uint16_t disablePage2WriteProtect = 0x128C;
26 static constexpr uint16_t disablePage3WriteProtect = 0x0082;
27
28 enum class MP2X6XXCmd : uint8_t
29 {
30 // Page 0 commands
31 readCRCReg = 0xED,
32 // Page 1 commands
33 mfrMTPMemoryCtrl = 0xCC,
34 // Page 2 commands
35 selectConfigCtrl = 0x1A,
36 // Page 3 commands
37 mfrMTPMemoryCtrlPage3 = 0x81,
38 };
39
parseDeviceConfiguration()40 sdbusplus::async::task<bool> MP2X6XX::parseDeviceConfiguration()
41 {
42 if (!configuration)
43 {
44 error("Device configuration not initialized");
45 co_return false;
46 }
47
48 configuration->vendorId = 0x4D5053;
49
50 for (const auto& tokens : parser->lineTokens)
51 {
52 if (!parser->isValidDataTokens(tokens))
53 {
54 continue;
55 }
56
57 auto regName = parser->getVal<std::string>(tokens, ATE::regName);
58 if (regName == productIdRegName)
59 {
60 configuration->productId =
61 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
62 configuration->configId =
63 parser->getVal<uint32_t>(tokens, ATE::configId);
64 }
65 else if (regName == crcUserRegName)
66 {
67 configuration->crcUser =
68 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
69 break;
70 }
71 }
72
73 co_return true;
74 }
75
verifyImage(const uint8_t * image,size_t imageSize)76 sdbusplus::async::task<bool> MP2X6XX::verifyImage(const uint8_t* image,
77 size_t imageSize)
78 {
79 if (!co_await parseImage(image, imageSize))
80 {
81 error("Image verification failed: image parsing failed");
82 co_return false;
83 }
84
85 if (configuration->registersData.empty())
86 {
87 error("Image verification failed - no data found");
88 co_return false;
89 }
90
91 if (configuration->productId == 0 || configuration->configId == 0)
92 {
93 error("Image verification failed - missing product or config ID");
94 co_return false;
95 }
96
97 co_return true;
98 }
99
checkId(PMBusCmd pmBusCmd,uint32_t expected)100 sdbusplus::async::task<bool> MP2X6XX::checkId(PMBusCmd pmBusCmd,
101 uint32_t expected)
102 {
103 const uint8_t cmd = static_cast<uint8_t>(pmBusCmd);
104 size_t idLen = 0;
105 bool blockRead = false;
106
107 switch (pmBusCmd)
108 {
109 case PMBusCmd::mfrId:
110 idLen = vendorIdLength;
111 blockRead = true;
112 break;
113 case PMBusCmd::icDeviceId:
114 idLen = deviceIdLength;
115 blockRead = true;
116 break;
117 case PMBusCmd::mfrSerial:
118 idLen = configIdLength;
119 break;
120 default:
121 error("Invalid command for ID check: {CMD}", "CMD", lg2::hex, cmd);
122 co_return false;
123 }
124
125 std::vector<uint8_t> rbuf;
126 std::vector<uint8_t> tbuf;
127
128 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
129 if (!i2cInterface.sendReceive(tbuf, rbuf))
130 {
131 error("Failed to set page 0 for ID check");
132 co_return false;
133 }
134
135 tbuf = {cmd};
136 rbuf.resize(idLen + (blockRead ? statusByteLength : 0));
137
138 if (!i2cInterface.sendReceive(tbuf, rbuf))
139 {
140 error("I2C failure during ID check, cmd {CMD}", "CMD", lg2::hex, cmd);
141 co_return false;
142 }
143
144 auto idBytes = std::span(rbuf).subspan(blockRead ? statusByteLength : 0);
145 uint32_t id = bytesToInt<uint32_t>(idBytes);
146
147 if (id != expected)
148 {
149 error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
150 lg2::hex, cmd, "ID", lg2::hex, id, "EXP", lg2::hex, expected);
151 co_return false;
152 }
153
154 co_return true;
155 }
156
unlockWriteProtect()157 sdbusplus::async::task<bool> MP2X6XX::unlockWriteProtect()
158 {
159 std::vector<uint8_t> tbuf;
160 std::vector<uint8_t> rbuf;
161
162 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
163 if (!i2cInterface.sendReceive(tbuf, rbuf))
164 {
165 error("Failed to set page 0 for unlocking write protect");
166 co_return false;
167 }
168
169 tbuf = buildByteVector(PMBusCmd::writeProtect, disableWriteProtect);
170 if (!i2cInterface.sendReceive(tbuf, rbuf))
171 {
172 error("Failed to disable write protect");
173 co_return false;
174 }
175
176 // unlock page 2 write protect
177 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
178 if (!i2cInterface.sendReceive(tbuf, rbuf))
179 {
180 error("Failed to set page 1 for unlocking write protect for page 2");
181 co_return false;
182 }
183
184 tbuf =
185 buildByteVector(MP2X6XXCmd::mfrMTPMemoryCtrl, disablePage2WriteProtect);
186 if (!i2cInterface.sendReceive(tbuf, rbuf))
187 {
188 error("Failed to unlock page 2 write protect");
189 co_return false;
190 }
191
192 // unlock page 3 write protect
193 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page3);
194 if (!i2cInterface.sendReceive(tbuf, rbuf))
195 {
196 error("Failed to set page 3 for unlocking write protect for page 3");
197 co_return false;
198 }
199
200 tbuf = buildByteVector(MP2X6XXCmd::mfrMTPMemoryCtrlPage3,
201 disablePage3WriteProtect);
202 if (!i2cInterface.sendReceive(tbuf, rbuf))
203 {
204 error("Failed to unlock page 3 write protect");
205 co_return false;
206 }
207
208 debug("Unlocked write protect");
209
210 co_return true;
211 }
212
selectConfig(uint8_t config)213 sdbusplus::async::task<bool> MP2X6XX::selectConfig(uint8_t config)
214 {
215 // MPS config select command:
216 // Writes to Page 2 @ 0x1A: value = 0x0F00 | ((config + 7) << 4)
217 // For config 1–6 → result: 0x0F80 to 0x0FD0
218
219 std::vector<uint8_t> tbuf;
220 std::vector<uint8_t> rbuf;
221
222 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
223 if (!i2cInterface.sendReceive(tbuf, rbuf))
224 {
225 error("Failed to set page 2 for configuration switch");
226 co_return false;
227 }
228
229 constexpr uint8_t baseOffset = 7;
230 uint8_t encodedNibble = static_cast<uint8_t>((config + baseOffset) << 4);
231 uint16_t command = 0x0F00 | encodedNibble;
232
233 tbuf = buildByteVector(MP2X6XXCmd::selectConfigCtrl, command);
234
235 if (!i2cInterface.sendReceive(tbuf, rbuf))
236 {
237 error("Failed to write config select command {CMD} for config {CONFIG}",
238 "CMD", lg2::hex, command, "CONFIG", config);
239 co_return false;
240 }
241
242 debug("Switched to config {CONFIG}", "CONFIG", config);
243 co_return true;
244 }
245
programConfigData(const std::vector<MPSData> & gdata)246 sdbusplus::async::task<bool> MP2X6XX::programConfigData(
247 const std::vector<MPSData>& gdata)
248 {
249 std::vector<uint8_t> tbuf;
250 std::vector<uint8_t> rbuf;
251
252 for (const auto& data : gdata)
253 {
254 uint8_t page = data.page & pageMask;
255
256 tbuf = buildByteVector(PMBusCmd::page, page);
257 if (!i2cInterface.sendReceive(tbuf, rbuf))
258 {
259 error("Failed to set page {PAGE} for register {REG}", "PAGE", page,
260 "REG", lg2::hex, data.addr);
261 co_return false;
262 }
263
264 tbuf = {data.addr};
265 tbuf.insert(tbuf.end(), data.data.begin(),
266 data.data.begin() + data.length);
267
268 if (!i2cInterface.sendReceive(tbuf, rbuf))
269 {
270 error(
271 "Failed to write data {DATA} to register {REG} on page {PAGE}",
272 "DATA", lg2::hex, bytesToInt<uint32_t>(data.data), "REG",
273 lg2::hex, data.addr, "PAGE", page);
274 co_return false;
275 }
276 }
277
278 if (!co_await storeUserCode())
279 {
280 error("Failed to store user code after programming config data");
281 co_return false;
282 }
283
284 co_return true;
285 }
286
configAllRegisters()287 sdbusplus::async::task<bool> MP2X6XX::configAllRegisters()
288 {
289 for (const auto& [config, gdata] : getGroupedConfigData(configMask, 4))
290 {
291 debug("Configuring registers for config {CONFIG}", "CONFIG", config);
292
293 // Select the appropriate config before programming its registers
294 if (config > 0 && !co_await selectConfig(config))
295 {
296 co_return false;
297 }
298
299 if (!co_await programConfigData(gdata))
300 {
301 error("Failed to program configuration {CONFIG}", "CONFIG", config);
302 co_return false;
303 }
304
305 debug("Configured {SIZE} registers for config {CONFIG}", "SIZE",
306 gdata.size(), "CONFIG", config);
307 }
308
309 co_return true;
310 }
311
storeUserCode()312 sdbusplus::async::task<bool> MP2X6XX::storeUserCode()
313 {
314 std::vector<uint8_t> tbuf;
315 std::vector<uint8_t> rbuf;
316
317 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
318 if (!i2cInterface.sendReceive(tbuf, rbuf))
319 {
320 error("Failed to set page 0 for storing user code");
321 co_return false;
322 }
323
324 tbuf = buildByteVector(PMBusCmd::storeUserCode);
325 if (!i2cInterface.sendReceive(tbuf, rbuf))
326 {
327 error("Failed to store user code");
328 co_return false;
329 }
330
331 // Wait store user code
332 co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
333
334 debug("Stored user code");
335
336 co_return true;
337 }
338
getCRC(uint32_t * checksum)339 sdbusplus::async::task<bool> MP2X6XX::getCRC(uint32_t* checksum)
340 {
341 if (checksum == nullptr)
342 {
343 error("getCRC() called with null checksum pointer");
344 co_return false;
345 }
346
347 std::vector<uint8_t> tbuf;
348 std::vector<uint8_t> rbuf;
349
350 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
351 if (!i2cInterface.sendReceive(tbuf, rbuf))
352 {
353 error("Failed to set page 0 for CRC read");
354 co_return false;
355 }
356
357 tbuf = buildByteVector(MP2X6XXCmd::readCRCReg);
358 rbuf.resize(crcLength);
359 if (!i2cInterface.sendReceive(tbuf, rbuf))
360 {
361 error("Failed to read CRC from device");
362 co_return false;
363 }
364
365 if (rbuf.size() < crcLength)
366 {
367 error("CRC read returned insufficient data");
368 co_return false;
369 }
370
371 *checksum = bytesToInt<uint32_t>(rbuf);
372
373 debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
374
375 co_return true;
376 }
377
checkMTPCRC()378 sdbusplus::async::task<bool> MP2X6XX::checkMTPCRC()
379 {
380 uint32_t crc = 0;
381 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
382 if (!co_await getCRC(&crc))
383 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
384 {
385 error("Failed to get CRC for MTP check");
386 co_return false;
387 }
388
389 debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
390 lg2::hex, configuration->crcUser);
391
392 co_return configuration->crcUser == crc;
393 }
394
forcedUpdateAllowed()395 bool MP2X6XX::forcedUpdateAllowed()
396 {
397 return true;
398 }
399
updateFirmware(bool force)400 sdbusplus::async::task<bool> MP2X6XX::updateFirmware(bool force)
401 {
402 (void)force;
403
404 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId))
405 {
406 co_return false;
407 }
408
409 if (!co_await checkId(PMBusCmd::icDeviceId, configuration->productId))
410 {
411 co_return false;
412 }
413
414 if (!co_await checkId(PMBusCmd::mfrSerial, configuration->configId))
415 {
416 co_return false;
417 }
418
419 if (!co_await unlockWriteProtect())
420 {
421 co_return false;
422 }
423
424 if (!co_await configAllRegisters())
425 {
426 co_return false;
427 }
428
429 if (!(co_await checkMTPCRC()))
430 {
431 co_return false;
432 }
433
434 co_return true;
435 }
436
437 } // namespace phosphor::software::VR
438