blob: 25a4f26360ffbe0a56bf8ea6132e7e7edca56214 [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;
43constexpr uint8_t MFRFwCmdReset = 0x0e;
44constexpr uint8_t MFRFwCmdRmng = 0x10;
45constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E;
46constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11;
47constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12;
48constexpr uint8_t MFRFwCmdGetCRC = 0x2D;
49constexpr int XDPE15284CConfSize = 1344;
50constexpr int XDPE19283BConfSize = 1416;
51constexpr 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;
58constexpr uint16_t VRResetDelay = 500;
59
60constexpr uint32_t CRC32Poly = 0xEDB88320;
Christopher Meis7e446a42024-10-22 09:36:41 +020061
62const char* const AddressField = "PMBus Address :";
63const char* const ChecksumField = "Checksum :";
Christopher Meisfd341442025-06-16 14:34:51 +020064const char* const DataStartTag = "[Configuration Data]";
Christopher Meis7e446a42024-10-22 09:36:41 +020065const char* const DataEndTag = "[End Configuration Data]";
66const char* const DataComment = "//";
67const char* const DataXV = "XV";
68
69XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
70 uint16_t address) :
71 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
72{}
73
Christopher Meis7e446a42024-10-22 09:36:41 +020074sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
Christopher Meis7e446a42024-10-22 09:36:41 +020075{
Christopher Meis7e446a42024-10-22 09:36:41 +020076 uint8_t tbuf[16] = {0};
77 tbuf[0] = PMBusICDeviceID;
78 tbuf[1] = 2;
79 uint8_t tSize = 1;
80 uint8_t rbuf[16] = {0};
81 uint8_t rSize = IFXICDeviceIDLen + 1;
82
Christopher Meisfd341442025-06-16 14:34:51 +020083 if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +020084 {
85 error("Failed to get device ID");
86 co_return false;
87 }
88
Christopher Meisfd341442025-06-16 14:34:51 +020089 // According to datasheet:
90 // rbuf[1]: device revision
91 // rbuf[2]: device id
Christopher Meis7e446a42024-10-22 09:36:41 +020092 std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
Christopher Meisfd341442025-06-16 14:34:51 +020093 info.deviceRev = deviceID[0];
94 info.deviceId = deviceID[1];
95 debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
96 debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
Christopher Meis7e446a42024-10-22 09:36:41 +020097
98 co_return true;
99}
100
Christopher Meisfd341442025-06-16 14:34:51 +0200101sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
102 uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
Christopher Meis7e446a42024-10-22 09:36:41 +0200103{
Christopher Meis7e446a42024-10-22 09:36:41 +0200104 uint8_t tBuf[16] = {0};
105 uint8_t rBuf[16] = {0};
106 uint8_t tSize = 0;
107 uint8_t rSize = 0;
108
109 if (data)
110 {
111 tBuf[0] = IFXMFRFwCmdData;
112 tBuf[1] = 4; // Block write 4 bytes
113 tSize = 6;
114 std::memcpy(&tBuf[2], data, 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200115 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
116 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200117 {
118 error("Failed to send MFR command: {CMD}", "CMD",
119 std::string("IFXMFRFwCmdDAta"));
120 co_return false;
121 }
122 }
123
124 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
125
126 tBuf[0] = IFXMFRFwCmd;
127 tBuf[1] = cmd;
128 tSize = 2;
129 rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200130 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200131 {
132 error("Failed to send MFR command: {CMD}", "CMD",
133 std::string("IFXMFRFwCmd"));
134 co_return false;
135 }
136
Christopher Meisfd341442025-06-16 14:34:51 +0200137 co_await sdbusplus::async::sleep_for(
138 ctx, std::chrono::milliseconds(processTime));
Christopher Meis7e446a42024-10-22 09:36:41 +0200139
140 if (resp)
141 {
142 tBuf[0] = IFXMFRFwCmdData;
143 tSize = 1;
144 rSize = 6;
Christopher Meisfd341442025-06-16 14:34:51 +0200145 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
146 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200147 {
148 error("Failed to send MFR command: {CMD}", "CMD",
149 std::string("IFXMFRFwCmdData"));
150 co_return false;
151 }
152 if (rBuf[0] != 4)
153 {
154 error(
155 "Failed to receive MFR response with unexpected response size");
156 co_return false;
157 }
158 std::memcpy(resp, rBuf + 1, 4);
159 }
160
161 co_return true;
162}
163
Christopher Meis7e446a42024-10-22 09:36:41 +0200164sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
Christopher Meis7e446a42024-10-22 09:36:41 +0200165{
Christopher Meisfd341442025-06-16 14:34:51 +0200166 // According to datasheet:
167 // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
Christopher Meis7e446a42024-10-22 09:36:41 +0200168 uint8_t tBuf[16] = {0};
169 uint8_t rBuf[16] = {0};
170 uint8_t devId[2] = {0};
171
Christopher Meisfd341442025-06-16 14:34:51 +0200172 if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
173 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200174 {
175 error("Failed to request remaining writes");
176 co_return false;
177 }
178
Christopher Meisfd341442025-06-16 14:34:51 +0200179 if (!(co_await this->getDeviceId(devId)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200180 {
181 error("Failed to request device ID for remaining writes");
182 co_return false;
183 }
184
185 int configSize = getConfigSize(devId[1], devId[0]);
186 if (configSize < 0)
187 {
188 error("Failed to request valid configuration size");
189 co_return false;
190 }
191
192 *remain = REMAINING_TIMES(rBuf, configSize);
193
Christopher Meisfd341442025-06-16 14:34:51 +0200194 co_return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200195}
196
197int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
198{
199 int size = -1;
200
201 switch (deviceId)
202 {
Christopher Meisfd341442025-06-16 14:34:51 +0200203 case ProductIDXDPE19283AC:
Christopher Meis7e446a42024-10-22 09:36:41 +0200204 if (revision == REV_B)
205 {
206 size = XDPE19283BConfSize;
207 }
208 break;
209 case ProductIDXDPE15284:
210 size = XDPE15284CConfSize;
211 break;
212 default:
213 error(
214 "Failed to get configuration size of {DEVID} with revision {REV}",
215 "DEVID", deviceId, "REV", revision);
216 return -1;
217 }
218
219 return size;
220}
221
Christopher Meis7e446a42024-10-22 09:36:41 +0200222sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
Christopher Meis7e446a42024-10-22 09:36:41 +0200223{
224 uint8_t tBuf[16] = {0};
225 uint8_t rBuf[16] = {0};
226
Christopher Meisfd341442025-06-16 14:34:51 +0200227 if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
228 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200229 {
230 error("Failed to get CRC value");
231 co_return false;
232 }
233
234 *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
235 (static_cast<uint32_t>(rBuf[2]) << 16) |
236 (static_cast<uint32_t>(rBuf[1]) << 8) |
237 (static_cast<uint32_t>(rBuf[0]));
238
239 co_return true;
240}
241
Christopher Meis7e446a42024-10-22 09:36:41 +0200242sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200243{
Christopher Meis7e446a42024-10-22 09:36:41 +0200244 uint8_t tBuf[16] = {0};
245 uint8_t rBuf[16] = {0};
246 uint8_t remain = 0;
247 uint32_t sum = 0;
248 int size = 0;
249
250 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200251 if (!(co_await getCRC(&sum)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200252 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200253 {
254 error("Failed to program the VR");
255 co_return -1;
256 }
257
Christopher Meisfd341442025-06-16 14:34:51 +0200258 debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
259 debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
260
Christopher Meis7e446a42024-10-22 09:36:41 +0200261 if (!force && (sum == configuration.sumExp))
262 {
263 error("Failed to program the VR - CRC value are equal with no force");
264 co_return -1;
265 }
266
267 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200268 if (!(co_await this->getRemainingWrites(&remain)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200269 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200270 {
271 error("Failed to program the VR - unable to obtain remaing writes");
272 co_return -1;
273 }
274
Christopher Meisfd341442025-06-16 14:34:51 +0200275 debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
276
Christopher Meis7e446a42024-10-22 09:36:41 +0200277 if (!remain)
278 {
279 error("Failed to program the VR - no remaining write cycles left");
280 co_return -1;
281 }
282
283 if (!force && (remain <= VRWarnRemaining))
284 {
285 error(
286 "Failed to program the VR - {REMAIN} remaining writes left and not force",
287 "REMAIN", remain);
288 co_return -1;
289 }
290
291 // Added reprogramming of the entire configuration file.
292 // Except for the trim section, all other data will be replaced.
293 // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
294 // and XVcode. If the old sections are not invalidated in OTP, they can
295 // affect the CRC calculation.
Christopher Meisfd341442025-06-16 14:34:51 +0200296 debug("Invalidate current Configuration");
Christopher Meis7e446a42024-10-22 09:36:41 +0200297
298 tBuf[0] = 0xfe;
299 tBuf[1] = 0xfe;
300 tBuf[2] = 0x00;
301 tBuf[3] = 0x00;
302
Christopher Meisfd341442025-06-16 14:34:51 +0200303 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
304 MFROTPFileInvalidationWaitTime, tBuf, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200305 {
Christopher Meisfd341442025-06-16 14:34:51 +0200306 error("Failed to program the VR - Invalidation of current FW");
307 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200308 }
309
Christopher Meis7e446a42024-10-22 09:36:41 +0200310 for (int i = 0; i < configuration.sectCnt; i++)
311 {
Christopher Meisfd341442025-06-16 14:34:51 +0200312 debug("Programming section: {SEC}", "SEC", i);
Christopher Meis7e446a42024-10-22 09:36:41 +0200313 struct configSect* sect = &configuration.section[i];
314 if (sect == NULL)
315 {
316 error(
317 "Failed to program the VR - unexpected NULL section in config");
Christopher Meisfd341442025-06-16 14:34:51 +0200318 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200319 }
320
321 if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
322 {
Christopher Meisfd341442025-06-16 14:34:51 +0200323 debug("Section Type: {TYPE}", "TYPE", lg2::hex,
324 configuration.section[i].type);
325
326 // clear bit0 of PMBUS_STS_CML
Christopher Meis7e446a42024-10-22 09:36:41 +0200327 tBuf[0] = PMBusSTLCml;
328 tBuf[1] = 0x1;
329 uint8_t tSize = 2;
330 uint8_t rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200331 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
332 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200333 {
334 error("Failed to program the VR on sendReceive {CMD}", "CMD",
335 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200336 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200337 }
338
Christopher Meisfd341442025-06-16 14:34:51 +0200339 debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
Christopher Meis7e446a42024-10-22 09:36:41 +0200340 tBuf[0] = sect->type;
341 tBuf[1] = 0x00;
342 tBuf[2] = 0x00;
343 tBuf[3] = 0x00;
344
Christopher Meisfd341442025-06-16 14:34:51 +0200345 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
346 MFRSectionInvalidationWaitTime, tBuf,
347 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200348 {
349 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
350 std::string("MFRFwCmdOTPFileInvd"));
Christopher Meisfd341442025-06-16 14:34:51 +0200351 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200352 }
353
Christopher Meisfd341442025-06-16 14:34:51 +0200354 // set scratchpad addr
355 // XDPE192XX Rev A/B: 0x2005e000
356 // Rev C : 0x2005e400
357 // Rev D : 0x2005f000
358
359 debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
360 info.scratchPadAddress);
Christopher Meis7e446a42024-10-22 09:36:41 +0200361
362 tBuf[0] = IFXMFRAHBAddr;
363 tBuf[1] = 4;
Christopher Meisfd341442025-06-16 14:34:51 +0200364 tBuf[2] = (info.scratchPadAddress) & 0xFF;
365 tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
366 tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
367 tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
Christopher Meis7e446a42024-10-22 09:36:41 +0200368 tSize = 6;
369 rSize = 0;
370
Christopher Meisfd341442025-06-16 14:34:51 +0200371 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
372 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200373 {
374 error("Failed to program VR on sendReceive on {CMD}", "CMD",
375 std::string("IFXMFRAHBAddr"));
Christopher Meisfd341442025-06-16 14:34:51 +0200376 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200377 }
378
379 co_await sdbusplus::async::sleep_for(
380 ctx, std::chrono::microseconds(10000));
381 size = 0;
382 }
383
384 // programm into scratchpad
385 for (int j = 0; j < sect->dataCnt; j++)
386 {
387 tBuf[0] = IFXMFRRegWrite;
388 tBuf[1] = 4;
389 uint8_t tSize = 6;
390 uint8_t rSize = 0;
391 memcpy(&tBuf[2], &sect->data[j], 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200392 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
393 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200394 {
395 error("Failed to program the VR on sendReceive {CMD}", "CMD",
396 std::string("IFXMFRRegWrite"));
Christopher Meisfd341442025-06-16 14:34:51 +0200397 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200398 }
Christopher Meisfd341442025-06-16 14:34:51 +0200399 co_await sdbusplus::async::sleep_for(ctx,
400 std::chrono::milliseconds(10));
Christopher Meis7e446a42024-10-22 09:36:41 +0200401 }
402
403 size += sect->dataCnt * 4;
404 if ((i + 1 >= configuration.sectCnt) ||
405 (sect->type != configuration.section[i + 1].type))
406 {
Christopher Meisfd341442025-06-16 14:34:51 +0200407 // wait for programming soak (2ms/byte, at least 200ms)
408 // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
409 uint16_t soakTime = 100 * ((size / 50) + 2);
410
Christopher Meis7e446a42024-10-22 09:36:41 +0200411 // Upload to scratchpad
Christopher Meisfd341442025-06-16 14:34:51 +0200412 debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
413 "TIME", soakTime);
Christopher Meis7e446a42024-10-22 09:36:41 +0200414 std::memcpy(tBuf, &size, 2);
415 tBuf[2] = 0x00;
416 tBuf[3] = 0x00;
Christopher Meisfd341442025-06-16 14:34:51 +0200417 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
418 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200419 {
420 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
421 std::string("MFRFwCmdOTPConfSTO"));
Christopher Meisfd341442025-06-16 14:34:51 +0200422 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200423 }
424
Christopher Meisfd341442025-06-16 14:34:51 +0200425 // Read status faults after programming
Christopher Meis7e446a42024-10-22 09:36:41 +0200426 tBuf[0] = PMBusSTLCml;
427 uint8_t tSize = 1;
428 uint8_t rSize = 1;
Christopher Meisfd341442025-06-16 14:34:51 +0200429 if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
430 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200431 {
432 error("Failed to program VR on sendReceive {CMD}", "CMD",
433 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200434 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200435 }
436 if (rBuf[0] & 0x01)
437 {
Christopher Meisfd341442025-06-16 14:34:51 +0200438 error("Failed to program VR - status fault indicated error");
439 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200440 }
441 }
442 }
443
Christopher Meis7e446a42024-10-22 09:36:41 +0200444 co_return true;
445}
446
447int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
448{
449 char* s = strtok(src, delim);
450 int size = 0;
451 int maxSz = 5;
452
453 while (s)
454 {
455 *dest++ = s;
456 if ((++size) >= maxSz)
457 {
458 break;
459 }
460 s = strtok(NULL, delim);
461 }
462
463 return size;
464}
465
Christopher Meisfd341442025-06-16 14:34:51 +0200466bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
Christopher Meis7e446a42024-10-22 09:36:41 +0200467{
468 size_t lenEndTag = strlen(DataEndTag);
469 size_t lenStartTag = strlen(DataStartTag);
470 size_t lenComment = strlen(DataComment);
471 size_t lenXV = strlen(DataXV);
472 size_t start = 0;
473 const int maxLineLength = 40;
474 char line[maxLineLength];
475 char* token = NULL;
476 bool isData = false;
477 char delim = ' ';
478 uint16_t offset;
479 uint8_t sectType = 0x0;
480 uint32_t dWord;
481 int dataCnt = 0;
482 int sectIndex = -1;
483
484 for (size_t i = 0; i < image_size; i++)
485 {
486 if (image[i] == '\n')
487 {
488 std::memcpy(line, image + start, i - start);
489 if (!strncmp(line, DataComment, lenComment))
490 {
491 token = line + lenComment;
492 if (!strncmp(token, DataXV, lenXV))
493 {
494 debug("Parsing: {OBJ}", "OBJ",
495 reinterpret_cast<const char*>(line));
496 }
497 start = i + 1;
498 continue;
499 }
500 if (!strncmp(line, DataEndTag, lenEndTag))
501 {
502 debug("Parsing: {OBJ}", "OBJ",
503 reinterpret_cast<const char*>(line));
504 break;
505 }
506 else if (isData)
507 {
508 char* tokenList[8] = {0};
509 int tokenSize = lineSplit(tokenList, line, &delim);
510 if (tokenSize < 1)
511 {
512 start = i + 1;
513 continue;
514 }
515
516 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
517 if (sectType == SectTrim && offset != 0x0)
518 {
519 continue;
520 }
521
522 for (int i = 1; i < tokenSize; i++)
523 {
524 dWord = (uint32_t)strtol(tokenList[i], NULL, 16);
525 if ((offset == 0x0) && (i == 1))
526 {
527 sectType = (uint8_t)dWord;
528 if (sectType == SectTrim)
529 {
530 break;
531 }
532 if ((++sectIndex) >= MaxSectCnt)
533 {
Christopher Meisfd341442025-06-16 14:34:51 +0200534 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200535 }
536
537 configuration.section[sectIndex].type = sectType;
538 configuration.sectCnt = sectIndex + 1;
539 dataCnt = 0;
540 }
541
542 if (dataCnt >= MaxSectDataCnt)
543 {
Christopher Meisfd341442025-06-16 14:34:51 +0200544 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200545 }
546
547 configuration.section[sectIndex].data[dataCnt++] = dWord;
548 configuration.section[sectIndex].dataCnt = dataCnt;
549 configuration.totalCnt++;
550 }
551 }
552 else
553 {
554 if ((token = strstr(line, AddressField)) != NULL)
555 {
556 if ((token = strstr(token, "0x")) != NULL)
557 {
558 configuration.addr =
559 (uint8_t)(strtoul(token, NULL, 16) << 1);
560 }
561 }
562 else if ((token = strstr(line, ChecksumField)) != NULL)
563 {
564 if ((token = strstr(token, "0x")) != NULL)
565 {
566 configuration.sumExp =
567 (uint32_t)strtoul(token, NULL, 16);
568 }
569 }
570 else if (!strncmp(line, DataStartTag, lenStartTag))
571 {
572 isData = true;
573 start = i + 1;
574 continue;
575 }
576 else
577 {
578 start = i + 1;
579 continue;
580 }
581 }
582 start = i + 1;
583 }
584 }
585
Christopher Meisfd341442025-06-16 14:34:51 +0200586 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200587}
588
Christopher Meisfd341442025-06-16 14:34:51 +0200589bool XDPE1X2XX::checkImage()
Christopher Meis7e446a42024-10-22 09:36:41 +0200590{
591 uint8_t i;
592 uint32_t crc;
593 uint32_t sum = 0;
594
595 for (i = 0; i < configuration.sectCnt; i++)
596 {
597 struct configSect* sect = &configuration.section[i];
598 if (sect == NULL)
599 {
600 error("Failed to check image - unexpected NULL section");
Christopher Meisfd341442025-06-16 14:34:51 +0200601 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200602 }
603
Christopher Meisfd341442025-06-16 14:34:51 +0200604 crc = calcCRC32(&sect->data[0], 2);
Christopher Meis7e446a42024-10-22 09:36:41 +0200605 if (crc != sect->data[2])
606 {
607 error("Failed to check image - first CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200608 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200609 }
610 sum += crc;
611
612 // check CRC of section data
613 crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
614 if (crc != sect->data[sect->dataCnt - 1])
615 {
616 error("Failed to check image - second CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200617 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200618 }
619 sum += crc;
620 }
621
622 if (sum != configuration.sumExp)
623 {
Christopher Meisfd341442025-06-16 14:34:51 +0200624 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
625 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
Christopher Meis7e446a42024-10-22 09:36:41 +0200626 error("Failed to check image - third CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200627 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200628 }
629
Christopher Meisfd341442025-06-16 14:34:51 +0200630 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200631}
632
Christopher Meis7e446a42024-10-22 09:36:41 +0200633sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
634 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200635{
Christopher Meisfd341442025-06-16 14:34:51 +0200636 if (!parseImage(image, imageSize))
Christopher Meis7e446a42024-10-22 09:36:41 +0200637 {
638 error("Failed to update firmware on parsing Image");
639 co_return false;
640 }
641
Christopher Meisfd341442025-06-16 14:34:51 +0200642 if (!checkImage())
Christopher Meis7e446a42024-10-22 09:36:41 +0200643 {
644 error("Failed to update firmware on check image");
645 co_return false;
646 }
647
648 co_return true;
649}
650
Christopher Meisfd341442025-06-16 14:34:51 +0200651sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
Christopher Meis7e446a42024-10-22 09:36:41 +0200652{
Christopher Meisfd341442025-06-16 14:34:51 +0200653 uint8_t tbuf[16] = {0};
654 uint8_t rbuf[16] = {0};
655
656 tbuf[0] = 0x02;
657 tbuf[1] = 0x00;
658 tbuf[2] = 0x00;
659 tbuf[3] = 0x00;
660
661 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
662 rbuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200663 {
Christopher Meisfd341442025-06-16 14:34:51 +0200664 error("mfrFWcmd call failed to retrieve scratchpad address");
Christopher Meis7e446a42024-10-22 09:36:41 +0200665 co_return false;
666 }
667
Christopher Meisfd341442025-06-16 14:34:51 +0200668 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
669 (static_cast<uint32_t>(rbuf[2]) << 16) |
670 (static_cast<uint32_t>(rbuf[1]) << 8) |
671 (static_cast<uint32_t>(rbuf[0]));
672
673 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
674 info.scratchPadAddress);
675
676 co_return true;
677}
678
679sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
680{
681 bool ret = true;
682 if (!(co_await getScratchPadAddress()))
683 {
684 error("Failed to retrieve scratchpad address");
685 co_return false;
686 }
687
688 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
689 ret = co_await program(force);
690 if (!ret)
691 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
692 {
693 error("Failed to update firmware on program");
694 }
695
696 info.deviceId = 0;
697 info.deviceRev = 0;
698 info.remainingWrites = 0;
699 info.scratchPadAddress = 0;
700 info.actualCRC = 0;
701 info.configSize = 0;
702
Christopher Meis7e446a42024-10-22 09:36:41 +0200703 // Reset the configuration
704 configuration.addr = 0;
705 configuration.totalCnt = 0;
706 configuration.sumExp = 0;
707 configuration.sectCnt = 0;
708 for (int i = 0; i <= MaxSectCnt - 1; i++)
709 {
710 configuration.section[i].type = 0;
711 configuration.section[i].dataCnt = 0;
712 for (int j = 0; j <= MaxSectDataCnt; j++)
713 {
714 configuration.section[i].data[j] = 0;
715 }
716 }
717
Christopher Meisfd341442025-06-16 14:34:51 +0200718 if (!ret)
719 {
720 co_return false;
721 }
722
Christopher Meis7e446a42024-10-22 09:36:41 +0200723 co_return true;
724}
725
Christopher Meis7e446a42024-10-22 09:36:41 +0200726sdbusplus::async::task<bool> XDPE1X2XX::reset()
Christopher Meis7e446a42024-10-22 09:36:41 +0200727{
Christopher Meisfd341442025-06-16 14:34:51 +0200728 if (!(co_await mfrFWcmd(MFRFwCmdReset, VRResetDelay, NULL, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200729 {
730 error("Failed to reset the VR");
731 co_return false;
732 }
733
Christopher Meis7e446a42024-10-22 09:36:41 +0200734 co_return true;
735}
736
737uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
738{
739 if (data == NULL)
740 {
741 return 0;
742 }
743
744 uint32_t crc = 0xFFFFFFFF;
745 for (int i = 0; i < len; i++)
746 {
747 crc ^= data[i];
748
749 for (int b = 0; b < 32; b++)
750 {
751 if (crc & 0x1)
752 {
753 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
754 }
755 else
756 {
757 crc >>= 1;
758 }
759 }
760 }
761
762 return ~crc;
763}
764
765bool XDPE1X2XX::forcedUpdateAllowed()
766{
767 return true;
768}
769
770} // namespace phosphor::software::VR