blob: 51e0352a0aacceb2fc5c56c5e47b93fec34bc351 [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;
Daniel Hsu7e0ecac2025-06-27 11:02:11 +080049constexpr int XDPE152XXConfSize = 1344;
50constexpr int XDPE152XXDConfSize = 1312;
51constexpr int XDPE192XXBConfSize = 1416; // Config(728) + PMBus(568) + SVID(120)
Christopher Meisfd341442025-06-16 14:34:51 +020052constexpr uint8_t VRWarnRemaining = 3;
53constexpr uint8_t SectTrim = 0x02;
54
55constexpr uint16_t MFRDefaultWaitTime = 20;
56constexpr uint16_t MFRGetHWAddressWaitTime = 5;
57constexpr uint16_t MFROTPFileInvalidationWaitTime = 100;
58constexpr uint16_t MFRSectionInvalidationWaitTime = 4;
59constexpr uint16_t VRResetDelay = 500;
60
61constexpr uint32_t CRC32Poly = 0xEDB88320;
Christopher Meis7e446a42024-10-22 09:36:41 +020062
63const char* const AddressField = "PMBus Address :";
64const char* const ChecksumField = "Checksum :";
Christopher Meisfd341442025-06-16 14:34:51 +020065const char* const DataStartTag = "[Configuration Data]";
Christopher Meis7e446a42024-10-22 09:36:41 +020066const char* const DataEndTag = "[End Configuration Data]";
67const char* const DataComment = "//";
68const char* const DataXV = "XV";
69
70XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
71 uint16_t address) :
72 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
73{}
74
Christopher Meis7e446a42024-10-22 09:36:41 +020075sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
Christopher Meis7e446a42024-10-22 09:36:41 +020076{
Christopher Meis7e446a42024-10-22 09:36:41 +020077 uint8_t tbuf[16] = {0};
78 tbuf[0] = PMBusICDeviceID;
79 tbuf[1] = 2;
80 uint8_t tSize = 1;
81 uint8_t rbuf[16] = {0};
82 uint8_t rSize = IFXICDeviceIDLen + 1;
83
Christopher Meisfd341442025-06-16 14:34:51 +020084 if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +020085 {
86 error("Failed to get device ID");
87 co_return false;
88 }
89
Christopher Meisfd341442025-06-16 14:34:51 +020090 // According to datasheet:
91 // rbuf[1]: device revision
92 // rbuf[2]: device id
Christopher Meis7e446a42024-10-22 09:36:41 +020093 std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
Christopher Meisfd341442025-06-16 14:34:51 +020094 info.deviceRev = deviceID[0];
95 info.deviceId = deviceID[1];
96 debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
97 debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
Christopher Meis7e446a42024-10-22 09:36:41 +020098
99 co_return true;
100}
101
Christopher Meisfd341442025-06-16 14:34:51 +0200102sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
103 uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
Christopher Meis7e446a42024-10-22 09:36:41 +0200104{
Christopher Meis7e446a42024-10-22 09:36:41 +0200105 uint8_t tBuf[16] = {0};
106 uint8_t rBuf[16] = {0};
107 uint8_t tSize = 0;
108 uint8_t rSize = 0;
109
110 if (data)
111 {
112 tBuf[0] = IFXMFRFwCmdData;
113 tBuf[1] = 4; // Block write 4 bytes
114 tSize = 6;
115 std::memcpy(&tBuf[2], data, 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200116 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
117 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200118 {
119 error("Failed to send MFR command: {CMD}", "CMD",
120 std::string("IFXMFRFwCmdDAta"));
121 co_return false;
122 }
123 }
124
125 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
126
127 tBuf[0] = IFXMFRFwCmd;
128 tBuf[1] = cmd;
129 tSize = 2;
130 rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200131 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200132 {
133 error("Failed to send MFR command: {CMD}", "CMD",
134 std::string("IFXMFRFwCmd"));
135 co_return false;
136 }
137
Christopher Meisfd341442025-06-16 14:34:51 +0200138 co_await sdbusplus::async::sleep_for(
139 ctx, std::chrono::milliseconds(processTime));
Christopher Meis7e446a42024-10-22 09:36:41 +0200140
141 if (resp)
142 {
143 tBuf[0] = IFXMFRFwCmdData;
144 tSize = 1;
145 rSize = 6;
Christopher Meisfd341442025-06-16 14:34:51 +0200146 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
147 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200148 {
149 error("Failed to send MFR command: {CMD}", "CMD",
150 std::string("IFXMFRFwCmdData"));
151 co_return false;
152 }
153 if (rBuf[0] != 4)
154 {
155 error(
156 "Failed to receive MFR response with unexpected response size");
157 co_return false;
158 }
159 std::memcpy(resp, rBuf + 1, 4);
160 }
161
162 co_return true;
163}
164
Christopher Meis7e446a42024-10-22 09:36:41 +0200165sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
Christopher Meis7e446a42024-10-22 09:36:41 +0200166{
Christopher Meisfd341442025-06-16 14:34:51 +0200167 // According to datasheet:
168 // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
Christopher Meis7e446a42024-10-22 09:36:41 +0200169 uint8_t tBuf[16] = {0};
170 uint8_t rBuf[16] = {0};
171 uint8_t devId[2] = {0};
172
Christopher Meisfd341442025-06-16 14:34:51 +0200173 if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
174 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200175 {
176 error("Failed to request remaining writes");
177 co_return false;
178 }
179
Christopher Meisfd341442025-06-16 14:34:51 +0200180 if (!(co_await this->getDeviceId(devId)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200181 {
182 error("Failed to request device ID for remaining writes");
183 co_return false;
184 }
185
186 int configSize = getConfigSize(devId[1], devId[0]);
187 if (configSize < 0)
188 {
189 error("Failed to request valid configuration size");
190 co_return false;
191 }
192
193 *remain = REMAINING_TIMES(rBuf, configSize);
194
Christopher Meisfd341442025-06-16 14:34:51 +0200195 co_return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200196}
197
198int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
199{
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800200 static const std::map<std::pair<uint8_t, uint8_t>, int> configSizeMap = {
201 {{ProductIDXDPE19283AC, REV_B}, XDPE192XXBConfSize},
202 {{ProductIDXDPE15284, REV_A}, XDPE152XXConfSize},
203 {{ProductIDXDPE15284, REV_B}, XDPE152XXConfSize},
204 {{ProductIDXDPE15284, REV_C}, XDPE152XXConfSize},
205 {{ProductIDXDPE15284, REV_D}, XDPE152XXDConfSize},
206 {{ProductIDXDPE192C3AC, REV_B}, XDPE192XXBConfSize},
207 };
Christopher Meis7e446a42024-10-22 09:36:41 +0200208
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800209 auto it = configSizeMap.find({deviceId, revision});
210 if (it != configSizeMap.end())
Christopher Meis7e446a42024-10-22 09:36:41 +0200211 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800212 return it->second;
Christopher Meis7e446a42024-10-22 09:36:41 +0200213 }
214
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800215 error("Failed to get configuration size of {DEVID} with revision {REV}",
216 "DEVID", deviceId, "REV", revision);
217 return -1;
Christopher Meis7e446a42024-10-22 09:36:41 +0200218}
219
Christopher Meis7e446a42024-10-22 09:36:41 +0200220sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
Christopher Meis7e446a42024-10-22 09:36:41 +0200221{
222 uint8_t tBuf[16] = {0};
223 uint8_t rBuf[16] = {0};
224
Christopher Meisfd341442025-06-16 14:34:51 +0200225 if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
226 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200227 {
228 error("Failed to get CRC value");
229 co_return false;
230 }
231
232 *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
233 (static_cast<uint32_t>(rBuf[2]) << 16) |
234 (static_cast<uint32_t>(rBuf[1]) << 8) |
235 (static_cast<uint32_t>(rBuf[0]));
236
237 co_return true;
238}
239
Christopher Meis7e446a42024-10-22 09:36:41 +0200240sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200241{
Christopher Meis7e446a42024-10-22 09:36:41 +0200242 uint8_t tBuf[16] = {0};
243 uint8_t rBuf[16] = {0};
244 uint8_t remain = 0;
245 uint32_t sum = 0;
246 int size = 0;
247
248 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200249 if (!(co_await getCRC(&sum)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200250 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200251 {
252 error("Failed to program the VR");
253 co_return -1;
254 }
255
Christopher Meisfd341442025-06-16 14:34:51 +0200256 debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
257 debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
258
Christopher Meis7e446a42024-10-22 09:36:41 +0200259 if (!force && (sum == configuration.sumExp))
260 {
261 error("Failed to program the VR - CRC value are equal with no force");
262 co_return -1;
263 }
264
265 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200266 if (!(co_await this->getRemainingWrites(&remain)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200267 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200268 {
269 error("Failed to program the VR - unable to obtain remaing writes");
270 co_return -1;
271 }
272
Christopher Meisfd341442025-06-16 14:34:51 +0200273 debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
274
Christopher Meis7e446a42024-10-22 09:36:41 +0200275 if (!remain)
276 {
277 error("Failed to program the VR - no remaining write cycles left");
278 co_return -1;
279 }
280
281 if (!force && (remain <= VRWarnRemaining))
282 {
283 error(
284 "Failed to program the VR - {REMAIN} remaining writes left and not force",
285 "REMAIN", remain);
286 co_return -1;
287 }
288
289 // Added reprogramming of the entire configuration file.
290 // Except for the trim section, all other data will be replaced.
291 // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
292 // and XVcode. If the old sections are not invalidated in OTP, they can
293 // affect the CRC calculation.
Christopher Meisfd341442025-06-16 14:34:51 +0200294 debug("Invalidate current Configuration");
Christopher Meis7e446a42024-10-22 09:36:41 +0200295
296 tBuf[0] = 0xfe;
297 tBuf[1] = 0xfe;
298 tBuf[2] = 0x00;
299 tBuf[3] = 0x00;
300
Christopher Meisfd341442025-06-16 14:34:51 +0200301 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
302 MFROTPFileInvalidationWaitTime, tBuf, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200303 {
Christopher Meisfd341442025-06-16 14:34:51 +0200304 error("Failed to program the VR - Invalidation of current FW");
305 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200306 }
307
Christopher Meis7e446a42024-10-22 09:36:41 +0200308 for (int i = 0; i < configuration.sectCnt; i++)
309 {
Christopher Meisfd341442025-06-16 14:34:51 +0200310 debug("Programming section: {SEC}", "SEC", i);
Christopher Meis7e446a42024-10-22 09:36:41 +0200311 struct configSect* sect = &configuration.section[i];
312 if (sect == NULL)
313 {
314 error(
315 "Failed to program the VR - unexpected NULL section in config");
Christopher Meisfd341442025-06-16 14:34:51 +0200316 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200317 }
318
319 if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
320 {
Christopher Meisfd341442025-06-16 14:34:51 +0200321 debug("Section Type: {TYPE}", "TYPE", lg2::hex,
322 configuration.section[i].type);
323
324 // clear bit0 of PMBUS_STS_CML
Christopher Meis7e446a42024-10-22 09:36:41 +0200325 tBuf[0] = PMBusSTLCml;
326 tBuf[1] = 0x1;
327 uint8_t tSize = 2;
328 uint8_t rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200329 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
330 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200331 {
332 error("Failed to program the VR on sendReceive {CMD}", "CMD",
333 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200334 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200335 }
336
Christopher Meisfd341442025-06-16 14:34:51 +0200337 debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
Christopher Meis7e446a42024-10-22 09:36:41 +0200338 tBuf[0] = sect->type;
339 tBuf[1] = 0x00;
340 tBuf[2] = 0x00;
341 tBuf[3] = 0x00;
342
Christopher Meisfd341442025-06-16 14:34:51 +0200343 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
344 MFRSectionInvalidationWaitTime, tBuf,
345 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200346 {
347 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
348 std::string("MFRFwCmdOTPFileInvd"));
Christopher Meisfd341442025-06-16 14:34:51 +0200349 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200350 }
351
Christopher Meisfd341442025-06-16 14:34:51 +0200352 // set scratchpad addr
353 // XDPE192XX Rev A/B: 0x2005e000
354 // Rev C : 0x2005e400
355 // Rev D : 0x2005f000
356
357 debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
358 info.scratchPadAddress);
Christopher Meis7e446a42024-10-22 09:36:41 +0200359
360 tBuf[0] = IFXMFRAHBAddr;
361 tBuf[1] = 4;
Christopher Meisfd341442025-06-16 14:34:51 +0200362 tBuf[2] = (info.scratchPadAddress) & 0xFF;
363 tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
364 tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
365 tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
Christopher Meis7e446a42024-10-22 09:36:41 +0200366 tSize = 6;
367 rSize = 0;
368
Christopher Meisfd341442025-06-16 14:34:51 +0200369 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
370 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200371 {
372 error("Failed to program VR on sendReceive on {CMD}", "CMD",
373 std::string("IFXMFRAHBAddr"));
Christopher Meisfd341442025-06-16 14:34:51 +0200374 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200375 }
376
377 co_await sdbusplus::async::sleep_for(
378 ctx, std::chrono::microseconds(10000));
379 size = 0;
380 }
381
382 // programm into scratchpad
383 for (int j = 0; j < sect->dataCnt; j++)
384 {
385 tBuf[0] = IFXMFRRegWrite;
386 tBuf[1] = 4;
387 uint8_t tSize = 6;
388 uint8_t rSize = 0;
389 memcpy(&tBuf[2], &sect->data[j], 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200390 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
391 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200392 {
393 error("Failed to program the VR on sendReceive {CMD}", "CMD",
394 std::string("IFXMFRRegWrite"));
Christopher Meisfd341442025-06-16 14:34:51 +0200395 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200396 }
Christopher Meisfd341442025-06-16 14:34:51 +0200397 co_await sdbusplus::async::sleep_for(ctx,
398 std::chrono::milliseconds(10));
Christopher Meis7e446a42024-10-22 09:36:41 +0200399 }
400
401 size += sect->dataCnt * 4;
402 if ((i + 1 >= configuration.sectCnt) ||
403 (sect->type != configuration.section[i + 1].type))
404 {
Christopher Meisfd341442025-06-16 14:34:51 +0200405 // wait for programming soak (2ms/byte, at least 200ms)
406 // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
407 uint16_t soakTime = 100 * ((size / 50) + 2);
408
Christopher Meis7e446a42024-10-22 09:36:41 +0200409 // Upload to scratchpad
Christopher Meisfd341442025-06-16 14:34:51 +0200410 debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
411 "TIME", soakTime);
Christopher Meis7e446a42024-10-22 09:36:41 +0200412 std::memcpy(tBuf, &size, 2);
413 tBuf[2] = 0x00;
414 tBuf[3] = 0x00;
Christopher Meisfd341442025-06-16 14:34:51 +0200415 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
416 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200417 {
418 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
419 std::string("MFRFwCmdOTPConfSTO"));
Christopher Meisfd341442025-06-16 14:34:51 +0200420 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200421 }
422
Christopher Meisfd341442025-06-16 14:34:51 +0200423 // Read status faults after programming
Christopher Meis7e446a42024-10-22 09:36:41 +0200424 tBuf[0] = PMBusSTLCml;
425 uint8_t tSize = 1;
426 uint8_t rSize = 1;
Christopher Meisfd341442025-06-16 14:34:51 +0200427 if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
428 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200429 {
430 error("Failed to program VR on sendReceive {CMD}", "CMD",
431 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200432 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200433 }
434 if (rBuf[0] & 0x01)
435 {
Christopher Meisfd341442025-06-16 14:34:51 +0200436 error("Failed to program VR - status fault indicated error");
437 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200438 }
439 }
440 }
441
Christopher Meis7e446a42024-10-22 09:36:41 +0200442 co_return true;
443}
444
445int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
446{
447 char* s = strtok(src, delim);
448 int size = 0;
449 int maxSz = 5;
450
451 while (s)
452 {
453 *dest++ = s;
454 if ((++size) >= maxSz)
455 {
456 break;
457 }
458 s = strtok(NULL, delim);
459 }
460
461 return size;
462}
463
Christopher Meisfd341442025-06-16 14:34:51 +0200464bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
Christopher Meis7e446a42024-10-22 09:36:41 +0200465{
466 size_t lenEndTag = strlen(DataEndTag);
467 size_t lenStartTag = strlen(DataStartTag);
468 size_t lenComment = strlen(DataComment);
469 size_t lenXV = strlen(DataXV);
470 size_t start = 0;
471 const int maxLineLength = 40;
472 char line[maxLineLength];
473 char* token = NULL;
474 bool isData = false;
475 char delim = ' ';
476 uint16_t offset;
477 uint8_t sectType = 0x0;
478 uint32_t dWord;
479 int dataCnt = 0;
480 int sectIndex = -1;
481
482 for (size_t i = 0; i < image_size; i++)
483 {
484 if (image[i] == '\n')
485 {
486 std::memcpy(line, image + start, i - start);
487 if (!strncmp(line, DataComment, lenComment))
488 {
489 token = line + lenComment;
490 if (!strncmp(token, DataXV, lenXV))
491 {
492 debug("Parsing: {OBJ}", "OBJ",
493 reinterpret_cast<const char*>(line));
494 }
495 start = i + 1;
496 continue;
497 }
498 if (!strncmp(line, DataEndTag, lenEndTag))
499 {
500 debug("Parsing: {OBJ}", "OBJ",
501 reinterpret_cast<const char*>(line));
502 break;
503 }
504 else if (isData)
505 {
506 char* tokenList[8] = {0};
507 int tokenSize = lineSplit(tokenList, line, &delim);
508 if (tokenSize < 1)
509 {
510 start = i + 1;
511 continue;
512 }
513
514 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
515 if (sectType == SectTrim && offset != 0x0)
516 {
517 continue;
518 }
519
520 for (int i = 1; i < tokenSize; i++)
521 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800522 dWord = (uint32_t)strtoul(tokenList[i], NULL, 16);
Christopher Meis7e446a42024-10-22 09:36:41 +0200523 if ((offset == 0x0) && (i == 1))
524 {
525 sectType = (uint8_t)dWord;
526 if (sectType == SectTrim)
527 {
528 break;
529 }
530 if ((++sectIndex) >= MaxSectCnt)
531 {
Christopher Meisfd341442025-06-16 14:34:51 +0200532 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200533 }
534
535 configuration.section[sectIndex].type = sectType;
536 configuration.sectCnt = sectIndex + 1;
537 dataCnt = 0;
538 }
539
540 if (dataCnt >= MaxSectDataCnt)
541 {
Christopher Meisfd341442025-06-16 14:34:51 +0200542 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200543 }
544
545 configuration.section[sectIndex].data[dataCnt++] = dWord;
546 configuration.section[sectIndex].dataCnt = dataCnt;
547 configuration.totalCnt++;
548 }
549 }
550 else
551 {
552 if ((token = strstr(line, AddressField)) != NULL)
553 {
554 if ((token = strstr(token, "0x")) != NULL)
555 {
556 configuration.addr =
557 (uint8_t)(strtoul(token, NULL, 16) << 1);
558 }
559 }
560 else if ((token = strstr(line, ChecksumField)) != NULL)
561 {
562 if ((token = strstr(token, "0x")) != NULL)
563 {
564 configuration.sumExp =
565 (uint32_t)strtoul(token, NULL, 16);
566 }
567 }
568 else if (!strncmp(line, DataStartTag, lenStartTag))
569 {
570 isData = true;
571 start = i + 1;
572 continue;
573 }
574 else
575 {
576 start = i + 1;
577 continue;
578 }
579 }
580 start = i + 1;
581 }
582 }
583
Christopher Meisfd341442025-06-16 14:34:51 +0200584 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200585}
586
Christopher Meisfd341442025-06-16 14:34:51 +0200587bool XDPE1X2XX::checkImage()
Christopher Meis7e446a42024-10-22 09:36:41 +0200588{
589 uint8_t i;
590 uint32_t crc;
591 uint32_t sum = 0;
592
593 for (i = 0; i < configuration.sectCnt; i++)
594 {
595 struct configSect* sect = &configuration.section[i];
596 if (sect == NULL)
597 {
598 error("Failed to check image - unexpected NULL section");
Christopher Meisfd341442025-06-16 14:34:51 +0200599 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200600 }
601
Christopher Meisfd341442025-06-16 14:34:51 +0200602 crc = calcCRC32(&sect->data[0], 2);
Christopher Meis7e446a42024-10-22 09:36:41 +0200603 if (crc != sect->data[2])
604 {
605 error("Failed to check image - first CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200606 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200607 }
608 sum += crc;
609
610 // check CRC of section data
611 crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
612 if (crc != sect->data[sect->dataCnt - 1])
613 {
614 error("Failed to check image - second CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200615 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200616 }
617 sum += crc;
618 }
619
620 if (sum != configuration.sumExp)
621 {
Christopher Meisfd341442025-06-16 14:34:51 +0200622 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
623 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
Christopher Meis7e446a42024-10-22 09:36:41 +0200624 error("Failed to check image - third CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200625 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200626 }
627
Christopher Meisfd341442025-06-16 14:34:51 +0200628 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200629}
630
Christopher Meis7e446a42024-10-22 09:36:41 +0200631sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
632 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200633{
Christopher Meisfd341442025-06-16 14:34:51 +0200634 if (!parseImage(image, imageSize))
Christopher Meis7e446a42024-10-22 09:36:41 +0200635 {
636 error("Failed to update firmware on parsing Image");
637 co_return false;
638 }
639
Christopher Meisfd341442025-06-16 14:34:51 +0200640 if (!checkImage())
Christopher Meis7e446a42024-10-22 09:36:41 +0200641 {
642 error("Failed to update firmware on check image");
643 co_return false;
644 }
645
646 co_return true;
647}
648
Christopher Meisfd341442025-06-16 14:34:51 +0200649sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
Christopher Meis7e446a42024-10-22 09:36:41 +0200650{
Christopher Meisfd341442025-06-16 14:34:51 +0200651 uint8_t tbuf[16] = {0};
652 uint8_t rbuf[16] = {0};
653
654 tbuf[0] = 0x02;
655 tbuf[1] = 0x00;
656 tbuf[2] = 0x00;
657 tbuf[3] = 0x00;
658
659 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
660 rbuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200661 {
Christopher Meisfd341442025-06-16 14:34:51 +0200662 error("mfrFWcmd call failed to retrieve scratchpad address");
Christopher Meis7e446a42024-10-22 09:36:41 +0200663 co_return false;
664 }
665
Christopher Meisfd341442025-06-16 14:34:51 +0200666 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
667 (static_cast<uint32_t>(rbuf[2]) << 16) |
668 (static_cast<uint32_t>(rbuf[1]) << 8) |
669 (static_cast<uint32_t>(rbuf[0]));
670
671 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
672 info.scratchPadAddress);
673
674 co_return true;
675}
676
677sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
678{
679 bool ret = true;
680 if (!(co_await getScratchPadAddress()))
681 {
682 error("Failed to retrieve scratchpad address");
683 co_return false;
684 }
685
686 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
687 ret = co_await program(force);
688 if (!ret)
689 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
690 {
691 error("Failed to update firmware on program");
692 }
693
694 info.deviceId = 0;
695 info.deviceRev = 0;
696 info.remainingWrites = 0;
697 info.scratchPadAddress = 0;
698 info.actualCRC = 0;
699 info.configSize = 0;
700
Christopher Meis7e446a42024-10-22 09:36:41 +0200701 // Reset the configuration
702 configuration.addr = 0;
703 configuration.totalCnt = 0;
704 configuration.sumExp = 0;
705 configuration.sectCnt = 0;
706 for (int i = 0; i <= MaxSectCnt - 1; i++)
707 {
708 configuration.section[i].type = 0;
709 configuration.section[i].dataCnt = 0;
710 for (int j = 0; j <= MaxSectDataCnt; j++)
711 {
712 configuration.section[i].data[j] = 0;
713 }
714 }
715
Christopher Meisfd341442025-06-16 14:34:51 +0200716 if (!ret)
717 {
718 co_return false;
719 }
720
Christopher Meis7e446a42024-10-22 09:36:41 +0200721 co_return true;
722}
723
Christopher Meis7e446a42024-10-22 09:36:41 +0200724sdbusplus::async::task<bool> XDPE1X2XX::reset()
Christopher Meis7e446a42024-10-22 09:36:41 +0200725{
Christopher Meisfd341442025-06-16 14:34:51 +0200726 if (!(co_await mfrFWcmd(MFRFwCmdReset, VRResetDelay, NULL, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200727 {
728 error("Failed to reset the VR");
729 co_return false;
730 }
731
Christopher Meis7e446a42024-10-22 09:36:41 +0200732 co_return true;
733}
734
735uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
736{
737 if (data == NULL)
738 {
739 return 0;
740 }
741
742 uint32_t crc = 0xFFFFFFFF;
743 for (int i = 0; i < len; i++)
744 {
745 crc ^= data[i];
746
747 for (int b = 0; b < 32; b++)
748 {
749 if (crc & 0x1)
750 {
751 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
752 }
753 else
754 {
755 crc >>= 1;
756 }
757 }
758 }
759
760 return ~crc;
761}
762
763bool XDPE1X2XX::forcedUpdateAllowed()
764{
765 return true;
766}
767
768} // namespace phosphor::software::VR