blob: 184c17b0e3bde264875a3f706125e8bd2936ddd2 [file] [log] [blame]
Christopher Meis7e446a42024-10-22 09:36:41 +02001#include "xdpe1x2xx.hpp"
2
3#include "common/include/i2c/i2c.hpp"
4
5#include <unistd.h>
6
7#include <phosphor-logging/lg2.hpp>
8
9#include <cstdio>
10
11#define REMAINING_TIMES(x, y) (((((x)[1]) << 8) | ((x)[0])) / (y))
12
13PHOSPHOR_LOG2_USING;
14
15namespace phosphor::software::VR
16{
17
Christopher Meis7e446a42024-10-22 09:36:41 +020018enum RevisionCode
19{
20 REV_A = 0x00,
21 REV_B,
22 REV_C,
23 REV_D,
24};
25
26enum ProductID
27{
Christopher Meisfd341442025-06-16 14:34:51 +020028 ProductIDXDPE15254 = 0x90, // Revision C,D
29 ProductIDXDPE15284 = 0x8A, // Revision A,B,C,D
30 ProductIDXDPE19283AC = 0x95, // Revision A,B,C
31 ProductIDXDPE19283D = 0xAE, // Revision D
32 ProductIDXDPE192C3AC = 0x96, // Revision A,B,C
33 ProductIDXDPE192C3D = 0xAF, // Revision D
Christopher Meis7e446a42024-10-22 09:36:41 +020034};
35
Christopher Meisfd341442025-06-16 14:34:51 +020036constexpr uint8_t PMBusICDeviceID = 0xAD;
37constexpr uint8_t PMBusSTLCml = 0x7E;
38constexpr uint8_t IFXICDeviceIDLen = 2;
39constexpr uint8_t IFXMFRAHBAddr = 0xCE;
40constexpr uint8_t IFXMFRRegWrite = 0xDE;
41constexpr uint8_t IFXMFRFwCmdData = 0xFD;
42constexpr uint8_t IFXMFRFwCmd = 0xFE;
Christopher Meisfd341442025-06-16 14:34:51 +020043constexpr uint8_t MFRFwCmdRmng = 0x10;
44constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E;
45constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11;
46constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12;
47constexpr uint8_t MFRFwCmdGetCRC = 0x2D;
Daniel Hsu7e0ecac2025-06-27 11:02:11 +080048constexpr int XDPE152XXConfSize = 1344;
49constexpr int XDPE152XXDConfSize = 1312;
50constexpr int XDPE192XXBConfSize = 1416; // Config(728) + PMBus(568) + SVID(120)
Christopher Meisfd341442025-06-16 14:34:51 +020051constexpr uint8_t VRWarnRemaining = 3;
52constexpr uint8_t SectTrim = 0x02;
53
54constexpr uint16_t MFRDefaultWaitTime = 20;
55constexpr uint16_t MFRGetHWAddressWaitTime = 5;
56constexpr uint16_t MFROTPFileInvalidationWaitTime = 100;
57constexpr uint16_t MFRSectionInvalidationWaitTime = 4;
Christopher Meisfd341442025-06-16 14:34:51 +020058
59constexpr uint32_t CRC32Poly = 0xEDB88320;
Christopher Meis7e446a42024-10-22 09:36:41 +020060
61const char* const AddressField = "PMBus Address :";
62const char* const ChecksumField = "Checksum :";
Christopher Meisfd341442025-06-16 14:34:51 +020063const char* const DataStartTag = "[Configuration Data]";
Christopher Meis7e446a42024-10-22 09:36:41 +020064const char* const DataEndTag = "[End Configuration Data]";
65const char* const DataComment = "//";
66const char* const DataXV = "XV";
67
68XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
69 uint16_t address) :
70 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
71{}
72
Christopher Meis7e446a42024-10-22 09:36:41 +020073sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
Christopher Meis7e446a42024-10-22 09:36:41 +020074{
Christopher Meis7e446a42024-10-22 09:36:41 +020075 uint8_t tbuf[16] = {0};
76 tbuf[0] = PMBusICDeviceID;
77 tbuf[1] = 2;
78 uint8_t tSize = 1;
79 uint8_t rbuf[16] = {0};
80 uint8_t rSize = IFXICDeviceIDLen + 1;
81
Christopher Meisfd341442025-06-16 14:34:51 +020082 if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +020083 {
84 error("Failed to get device ID");
85 co_return false;
86 }
87
Christopher Meisfd341442025-06-16 14:34:51 +020088 // According to datasheet:
89 // rbuf[1]: device revision
90 // rbuf[2]: device id
Christopher Meis7e446a42024-10-22 09:36:41 +020091 std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
Christopher Meisfd341442025-06-16 14:34:51 +020092 info.deviceRev = deviceID[0];
93 info.deviceId = deviceID[1];
94 debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
95 debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
Christopher Meis7e446a42024-10-22 09:36:41 +020096
97 co_return true;
98}
99
Christopher Meisfd341442025-06-16 14:34:51 +0200100sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
101 uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
Christopher Meis7e446a42024-10-22 09:36:41 +0200102{
Christopher Meis7e446a42024-10-22 09:36:41 +0200103 uint8_t tBuf[16] = {0};
104 uint8_t rBuf[16] = {0};
105 uint8_t tSize = 0;
106 uint8_t rSize = 0;
107
108 if (data)
109 {
110 tBuf[0] = IFXMFRFwCmdData;
111 tBuf[1] = 4; // Block write 4 bytes
112 tSize = 6;
113 std::memcpy(&tBuf[2], data, 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200114 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
115 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200116 {
117 error("Failed to send MFR command: {CMD}", "CMD",
118 std::string("IFXMFRFwCmdDAta"));
119 co_return false;
120 }
121 }
122
123 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
124
125 tBuf[0] = IFXMFRFwCmd;
126 tBuf[1] = cmd;
127 tSize = 2;
128 rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200129 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200130 {
131 error("Failed to send MFR command: {CMD}", "CMD",
132 std::string("IFXMFRFwCmd"));
133 co_return false;
134 }
135
Christopher Meisfd341442025-06-16 14:34:51 +0200136 co_await sdbusplus::async::sleep_for(
137 ctx, std::chrono::milliseconds(processTime));
Christopher Meis7e446a42024-10-22 09:36:41 +0200138
139 if (resp)
140 {
141 tBuf[0] = IFXMFRFwCmdData;
142 tSize = 1;
143 rSize = 6;
Christopher Meisfd341442025-06-16 14:34:51 +0200144 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
145 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200146 {
147 error("Failed to send MFR command: {CMD}", "CMD",
148 std::string("IFXMFRFwCmdData"));
149 co_return false;
150 }
151 if (rBuf[0] != 4)
152 {
153 error(
154 "Failed to receive MFR response with unexpected response size");
155 co_return false;
156 }
157 std::memcpy(resp, rBuf + 1, 4);
158 }
159
160 co_return true;
161}
162
Christopher Meis7e446a42024-10-22 09:36:41 +0200163sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
Christopher Meis7e446a42024-10-22 09:36:41 +0200164{
Christopher Meisfd341442025-06-16 14:34:51 +0200165 // According to datasheet:
166 // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
Christopher Meis7e446a42024-10-22 09:36:41 +0200167 uint8_t tBuf[16] = {0};
168 uint8_t rBuf[16] = {0};
169 uint8_t devId[2] = {0};
170
Christopher Meisfd341442025-06-16 14:34:51 +0200171 if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
172 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200173 {
174 error("Failed to request remaining writes");
175 co_return false;
176 }
177
Christopher Meisfd341442025-06-16 14:34:51 +0200178 if (!(co_await this->getDeviceId(devId)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200179 {
180 error("Failed to request device ID for remaining writes");
181 co_return false;
182 }
183
184 int configSize = getConfigSize(devId[1], devId[0]);
185 if (configSize < 0)
186 {
187 error("Failed to request valid configuration size");
188 co_return false;
189 }
190
191 *remain = REMAINING_TIMES(rBuf, configSize);
192
Christopher Meisfd341442025-06-16 14:34:51 +0200193 co_return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200194}
195
196int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
197{
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800198 static const std::map<std::pair<uint8_t, uint8_t>, int> configSizeMap = {
199 {{ProductIDXDPE19283AC, REV_B}, XDPE192XXBConfSize},
200 {{ProductIDXDPE15284, REV_A}, XDPE152XXConfSize},
201 {{ProductIDXDPE15284, REV_B}, XDPE152XXConfSize},
202 {{ProductIDXDPE15284, REV_C}, XDPE152XXConfSize},
203 {{ProductIDXDPE15284, REV_D}, XDPE152XXDConfSize},
204 {{ProductIDXDPE192C3AC, REV_B}, XDPE192XXBConfSize},
205 };
Christopher Meis7e446a42024-10-22 09:36:41 +0200206
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800207 auto it = configSizeMap.find({deviceId, revision});
208 if (it != configSizeMap.end())
Christopher Meis7e446a42024-10-22 09:36:41 +0200209 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800210 return it->second;
Christopher Meis7e446a42024-10-22 09:36:41 +0200211 }
212
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800213 error("Failed to get configuration size of {DEVID} with revision {REV}",
214 "DEVID", deviceId, "REV", revision);
215 return -1;
Christopher Meis7e446a42024-10-22 09:36:41 +0200216}
217
Christopher Meis7e446a42024-10-22 09:36:41 +0200218sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
Christopher Meis7e446a42024-10-22 09:36:41 +0200219{
220 uint8_t tBuf[16] = {0};
221 uint8_t rBuf[16] = {0};
222
Christopher Meisfd341442025-06-16 14:34:51 +0200223 if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
224 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200225 {
226 error("Failed to get CRC value");
227 co_return false;
228 }
229
230 *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
231 (static_cast<uint32_t>(rBuf[2]) << 16) |
232 (static_cast<uint32_t>(rBuf[1]) << 8) |
233 (static_cast<uint32_t>(rBuf[0]));
234
235 co_return true;
236}
237
Christopher Meis7e446a42024-10-22 09:36:41 +0200238sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200239{
Christopher Meis7e446a42024-10-22 09:36:41 +0200240 uint8_t tBuf[16] = {0};
241 uint8_t rBuf[16] = {0};
242 uint8_t remain = 0;
243 uint32_t sum = 0;
244 int size = 0;
245
246 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200247 if (!(co_await getCRC(&sum)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200248 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200249 {
250 error("Failed to program the VR");
251 co_return -1;
252 }
253
Christopher Meisfd341442025-06-16 14:34:51 +0200254 debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
255 debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
256
Christopher Meis7e446a42024-10-22 09:36:41 +0200257 if (!force && (sum == configuration.sumExp))
258 {
259 error("Failed to program the VR - CRC value are equal with no force");
260 co_return -1;
261 }
262
263 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200264 if (!(co_await this->getRemainingWrites(&remain)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200265 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200266 {
267 error("Failed to program the VR - unable to obtain remaing writes");
268 co_return -1;
269 }
270
Christopher Meisfd341442025-06-16 14:34:51 +0200271 debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
272
Christopher Meis7e446a42024-10-22 09:36:41 +0200273 if (!remain)
274 {
275 error("Failed to program the VR - no remaining write cycles left");
276 co_return -1;
277 }
278
279 if (!force && (remain <= VRWarnRemaining))
280 {
281 error(
282 "Failed to program the VR - {REMAIN} remaining writes left and not force",
283 "REMAIN", remain);
284 co_return -1;
285 }
286
287 // Added reprogramming of the entire configuration file.
288 // Except for the trim section, all other data will be replaced.
289 // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
290 // and XVcode. If the old sections are not invalidated in OTP, they can
291 // affect the CRC calculation.
Christopher Meisfd341442025-06-16 14:34:51 +0200292 debug("Invalidate current Configuration");
Christopher Meis7e446a42024-10-22 09:36:41 +0200293
294 tBuf[0] = 0xfe;
295 tBuf[1] = 0xfe;
296 tBuf[2] = 0x00;
297 tBuf[3] = 0x00;
298
Christopher Meisfd341442025-06-16 14:34:51 +0200299 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
300 MFROTPFileInvalidationWaitTime, tBuf, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200301 {
Christopher Meisfd341442025-06-16 14:34:51 +0200302 error("Failed to program the VR - Invalidation of current FW");
303 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200304 }
305
Christopher Meis7e446a42024-10-22 09:36:41 +0200306 for (int i = 0; i < configuration.sectCnt; i++)
307 {
Christopher Meisfd341442025-06-16 14:34:51 +0200308 debug("Programming section: {SEC}", "SEC", i);
Christopher Meis7e446a42024-10-22 09:36:41 +0200309 struct configSect* sect = &configuration.section[i];
310 if (sect == NULL)
311 {
312 error(
313 "Failed to program the VR - unexpected NULL section in config");
Christopher Meisfd341442025-06-16 14:34:51 +0200314 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200315 }
316
317 if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
318 {
Christopher Meisfd341442025-06-16 14:34:51 +0200319 debug("Section Type: {TYPE}", "TYPE", lg2::hex,
320 configuration.section[i].type);
321
322 // clear bit0 of PMBUS_STS_CML
Christopher Meis7e446a42024-10-22 09:36:41 +0200323 tBuf[0] = PMBusSTLCml;
324 tBuf[1] = 0x1;
325 uint8_t tSize = 2;
326 uint8_t rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200327 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
328 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200329 {
330 error("Failed to program the VR on sendReceive {CMD}", "CMD",
331 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200332 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200333 }
334
Christopher Meisfd341442025-06-16 14:34:51 +0200335 debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
Christopher Meis7e446a42024-10-22 09:36:41 +0200336 tBuf[0] = sect->type;
337 tBuf[1] = 0x00;
338 tBuf[2] = 0x00;
339 tBuf[3] = 0x00;
340
Christopher Meisfd341442025-06-16 14:34:51 +0200341 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
342 MFRSectionInvalidationWaitTime, tBuf,
343 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200344 {
345 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
346 std::string("MFRFwCmdOTPFileInvd"));
Christopher Meisfd341442025-06-16 14:34:51 +0200347 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200348 }
349
Christopher Meisfd341442025-06-16 14:34:51 +0200350 // set scratchpad addr
351 // XDPE192XX Rev A/B: 0x2005e000
352 // Rev C : 0x2005e400
353 // Rev D : 0x2005f000
354
355 debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
356 info.scratchPadAddress);
Christopher Meis7e446a42024-10-22 09:36:41 +0200357
358 tBuf[0] = IFXMFRAHBAddr;
359 tBuf[1] = 4;
Christopher Meisfd341442025-06-16 14:34:51 +0200360 tBuf[2] = (info.scratchPadAddress) & 0xFF;
361 tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
362 tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
363 tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
Christopher Meis7e446a42024-10-22 09:36:41 +0200364 tSize = 6;
365 rSize = 0;
366
Christopher Meisfd341442025-06-16 14:34:51 +0200367 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
368 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200369 {
370 error("Failed to program VR on sendReceive on {CMD}", "CMD",
371 std::string("IFXMFRAHBAddr"));
Christopher Meisfd341442025-06-16 14:34:51 +0200372 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200373 }
374
375 co_await sdbusplus::async::sleep_for(
376 ctx, std::chrono::microseconds(10000));
377 size = 0;
378 }
379
380 // programm into scratchpad
381 for (int j = 0; j < sect->dataCnt; j++)
382 {
383 tBuf[0] = IFXMFRRegWrite;
384 tBuf[1] = 4;
385 uint8_t tSize = 6;
386 uint8_t rSize = 0;
387 memcpy(&tBuf[2], &sect->data[j], 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200388 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
389 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200390 {
391 error("Failed to program the VR on sendReceive {CMD}", "CMD",
392 std::string("IFXMFRRegWrite"));
Christopher Meisfd341442025-06-16 14:34:51 +0200393 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200394 }
Christopher Meisfd341442025-06-16 14:34:51 +0200395 co_await sdbusplus::async::sleep_for(ctx,
396 std::chrono::milliseconds(10));
Christopher Meis7e446a42024-10-22 09:36:41 +0200397 }
398
399 size += sect->dataCnt * 4;
400 if ((i + 1 >= configuration.sectCnt) ||
401 (sect->type != configuration.section[i + 1].type))
402 {
Christopher Meisfd341442025-06-16 14:34:51 +0200403 // wait for programming soak (2ms/byte, at least 200ms)
404 // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
405 uint16_t soakTime = 100 * ((size / 50) + 2);
406
Christopher Meis7e446a42024-10-22 09:36:41 +0200407 // Upload to scratchpad
Christopher Meisfd341442025-06-16 14:34:51 +0200408 debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
409 "TIME", soakTime);
Christopher Meis7e446a42024-10-22 09:36:41 +0200410 std::memcpy(tBuf, &size, 2);
411 tBuf[2] = 0x00;
412 tBuf[3] = 0x00;
Christopher Meisfd341442025-06-16 14:34:51 +0200413 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
414 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200415 {
416 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
417 std::string("MFRFwCmdOTPConfSTO"));
Christopher Meisfd341442025-06-16 14:34:51 +0200418 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200419 }
420
Christopher Meisfd341442025-06-16 14:34:51 +0200421 // Read status faults after programming
Christopher Meis7e446a42024-10-22 09:36:41 +0200422 tBuf[0] = PMBusSTLCml;
423 uint8_t tSize = 1;
424 uint8_t rSize = 1;
Christopher Meisfd341442025-06-16 14:34:51 +0200425 if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
426 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200427 {
428 error("Failed to program VR on sendReceive {CMD}", "CMD",
429 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200430 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200431 }
432 if (rBuf[0] & 0x01)
433 {
Christopher Meisfd341442025-06-16 14:34:51 +0200434 error("Failed to program VR - status fault indicated error");
435 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200436 }
437 }
438 }
439
Christopher Meis7e446a42024-10-22 09:36:41 +0200440 co_return true;
441}
442
443int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
444{
445 char* s = strtok(src, delim);
446 int size = 0;
447 int maxSz = 5;
448
449 while (s)
450 {
451 *dest++ = s;
452 if ((++size) >= maxSz)
453 {
454 break;
455 }
456 s = strtok(NULL, delim);
457 }
458
459 return size;
460}
461
Christopher Meisfd341442025-06-16 14:34:51 +0200462bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
Christopher Meis7e446a42024-10-22 09:36:41 +0200463{
464 size_t lenEndTag = strlen(DataEndTag);
465 size_t lenStartTag = strlen(DataStartTag);
466 size_t lenComment = strlen(DataComment);
467 size_t lenXV = strlen(DataXV);
468 size_t start = 0;
469 const int maxLineLength = 40;
470 char line[maxLineLength];
471 char* token = NULL;
472 bool isData = false;
473 char delim = ' ';
474 uint16_t offset;
475 uint8_t sectType = 0x0;
476 uint32_t dWord;
477 int dataCnt = 0;
478 int sectIndex = -1;
479
480 for (size_t i = 0; i < image_size; i++)
481 {
482 if (image[i] == '\n')
483 {
Leo Yangecee4a62025-10-30 15:44:38 +0800484 const size_t lineLength = i - start;
485 if (lineLength >= maxLineLength)
486 {
487 error("line length exceeded 40, please check image file.");
488 return false;
489 }
490 std::memcpy(line, image + start, lineLength);
491 line[lineLength] = '\0';
492
Christopher Meis7e446a42024-10-22 09:36:41 +0200493 if (!strncmp(line, DataComment, lenComment))
494 {
495 token = line + lenComment;
496 if (!strncmp(token, DataXV, lenXV))
497 {
498 debug("Parsing: {OBJ}", "OBJ",
499 reinterpret_cast<const char*>(line));
500 }
501 start = i + 1;
502 continue;
503 }
504 if (!strncmp(line, DataEndTag, lenEndTag))
505 {
506 debug("Parsing: {OBJ}", "OBJ",
507 reinterpret_cast<const char*>(line));
508 break;
509 }
510 else if (isData)
511 {
512 char* tokenList[8] = {0};
513 int tokenSize = lineSplit(tokenList, line, &delim);
514 if (tokenSize < 1)
515 {
516 start = i + 1;
517 continue;
518 }
519
520 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
521 if (sectType == SectTrim && offset != 0x0)
522 {
523 continue;
524 }
525
526 for (int i = 1; i < tokenSize; i++)
527 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800528 dWord = (uint32_t)strtoul(tokenList[i], NULL, 16);
Christopher Meis7e446a42024-10-22 09:36:41 +0200529 if ((offset == 0x0) && (i == 1))
530 {
531 sectType = (uint8_t)dWord;
532 if (sectType == SectTrim)
533 {
534 break;
535 }
536 if ((++sectIndex) >= MaxSectCnt)
537 {
Christopher Meisfd341442025-06-16 14:34:51 +0200538 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200539 }
540
541 configuration.section[sectIndex].type = sectType;
542 configuration.sectCnt = sectIndex + 1;
543 dataCnt = 0;
544 }
545
546 if (dataCnt >= MaxSectDataCnt)
547 {
Christopher Meisfd341442025-06-16 14:34:51 +0200548 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200549 }
550
551 configuration.section[sectIndex].data[dataCnt++] = dWord;
552 configuration.section[sectIndex].dataCnt = dataCnt;
553 configuration.totalCnt++;
554 }
555 }
556 else
557 {
558 if ((token = strstr(line, AddressField)) != NULL)
559 {
560 if ((token = strstr(token, "0x")) != NULL)
561 {
562 configuration.addr =
563 (uint8_t)(strtoul(token, NULL, 16) << 1);
564 }
565 }
566 else if ((token = strstr(line, ChecksumField)) != NULL)
567 {
568 if ((token = strstr(token, "0x")) != NULL)
569 {
570 configuration.sumExp =
571 (uint32_t)strtoul(token, NULL, 16);
572 }
573 }
574 else if (!strncmp(line, DataStartTag, lenStartTag))
575 {
576 isData = true;
577 start = i + 1;
578 continue;
579 }
580 else
581 {
582 start = i + 1;
583 continue;
584 }
585 }
586 start = i + 1;
587 }
588 }
589
Christopher Meisfd341442025-06-16 14:34:51 +0200590 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200591}
592
Christopher Meisfd341442025-06-16 14:34:51 +0200593bool XDPE1X2XX::checkImage()
Christopher Meis7e446a42024-10-22 09:36:41 +0200594{
595 uint8_t i;
596 uint32_t crc;
597 uint32_t sum = 0;
598
599 for (i = 0; i < configuration.sectCnt; i++)
600 {
601 struct configSect* sect = &configuration.section[i];
602 if (sect == NULL)
603 {
604 error("Failed to check image - unexpected NULL section");
Christopher Meisfd341442025-06-16 14:34:51 +0200605 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200606 }
607
Christopher Meisfd341442025-06-16 14:34:51 +0200608 crc = calcCRC32(&sect->data[0], 2);
Christopher Meis7e446a42024-10-22 09:36:41 +0200609 if (crc != sect->data[2])
610 {
611 error("Failed to check image - first CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200612 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200613 }
614 sum += crc;
615
616 // check CRC of section data
617 crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
618 if (crc != sect->data[sect->dataCnt - 1])
619 {
620 error("Failed to check image - second CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200621 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200622 }
623 sum += crc;
624 }
625
626 if (sum != configuration.sumExp)
627 {
Christopher Meisfd341442025-06-16 14:34:51 +0200628 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
629 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
Christopher Meis7e446a42024-10-22 09:36:41 +0200630 error("Failed to check image - third CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200631 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200632 }
633
Christopher Meisfd341442025-06-16 14:34:51 +0200634 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200635}
636
Christopher Meis7e446a42024-10-22 09:36:41 +0200637sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
638 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200639{
Christopher Meisfd341442025-06-16 14:34:51 +0200640 if (!parseImage(image, imageSize))
Christopher Meis7e446a42024-10-22 09:36:41 +0200641 {
642 error("Failed to update firmware on parsing Image");
643 co_return false;
644 }
645
Christopher Meisfd341442025-06-16 14:34:51 +0200646 if (!checkImage())
Christopher Meis7e446a42024-10-22 09:36:41 +0200647 {
648 error("Failed to update firmware on check image");
649 co_return false;
650 }
651
652 co_return true;
653}
654
Christopher Meisfd341442025-06-16 14:34:51 +0200655sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
Christopher Meis7e446a42024-10-22 09:36:41 +0200656{
Christopher Meisfd341442025-06-16 14:34:51 +0200657 uint8_t tbuf[16] = {0};
658 uint8_t rbuf[16] = {0};
659
660 tbuf[0] = 0x02;
661 tbuf[1] = 0x00;
662 tbuf[2] = 0x00;
663 tbuf[3] = 0x00;
664
665 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
666 rbuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200667 {
Christopher Meisfd341442025-06-16 14:34:51 +0200668 error("mfrFWcmd call failed to retrieve scratchpad address");
Christopher Meis7e446a42024-10-22 09:36:41 +0200669 co_return false;
670 }
671
Christopher Meisfd341442025-06-16 14:34:51 +0200672 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
673 (static_cast<uint32_t>(rbuf[2]) << 16) |
674 (static_cast<uint32_t>(rbuf[1]) << 8) |
675 (static_cast<uint32_t>(rbuf[0]));
676
677 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
678 info.scratchPadAddress);
679
680 co_return true;
681}
682
683sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
684{
685 bool ret = true;
686 if (!(co_await getScratchPadAddress()))
687 {
688 error("Failed to retrieve scratchpad address");
689 co_return false;
690 }
691
692 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
693 ret = co_await program(force);
694 if (!ret)
695 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
696 {
697 error("Failed to update firmware on program");
698 }
699
700 info.deviceId = 0;
701 info.deviceRev = 0;
702 info.remainingWrites = 0;
703 info.scratchPadAddress = 0;
704 info.actualCRC = 0;
705 info.configSize = 0;
706
Christopher Meis7e446a42024-10-22 09:36:41 +0200707 // Reset the configuration
708 configuration.addr = 0;
709 configuration.totalCnt = 0;
710 configuration.sumExp = 0;
711 configuration.sectCnt = 0;
712 for (int i = 0; i <= MaxSectCnt - 1; i++)
713 {
714 configuration.section[i].type = 0;
715 configuration.section[i].dataCnt = 0;
Leo Yangecee4a62025-10-30 15:44:38 +0800716 for (int j = 0; j < MaxSectDataCnt; j++)
Christopher Meis7e446a42024-10-22 09:36:41 +0200717 {
718 configuration.section[i].data[j] = 0;
719 }
720 }
721
Christopher Meisfd341442025-06-16 14:34:51 +0200722 if (!ret)
723 {
724 co_return false;
725 }
726
Christopher Meis7e446a42024-10-22 09:36:41 +0200727 co_return true;
728}
729
Christopher Meis7e446a42024-10-22 09:36:41 +0200730uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
731{
732 if (data == NULL)
733 {
734 return 0;
735 }
736
737 uint32_t crc = 0xFFFFFFFF;
738 for (int i = 0; i < len; i++)
739 {
740 crc ^= data[i];
741
742 for (int b = 0; b < 32; b++)
743 {
744 if (crc & 0x1)
745 {
746 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
747 }
748 else
749 {
750 crc >>= 1;
751 }
752 }
753 }
754
755 return ~crc;
756}
757
758bool XDPE1X2XX::forcedUpdateAllowed()
759{
760 return true;
761}
762
763} // namespace phosphor::software::VR