blob: 206aef373e6553266f75948392e1d284460cd5fa [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,
Leo Yangfd204782025-10-30 13:42:02 +080024 REV_E,
Christopher Meis7e446a42024-10-22 09:36:41 +020025};
26
27enum ProductID
28{
Christopher Meisfd341442025-06-16 14:34:51 +020029 ProductIDXDPE15254 = 0x90, // Revision C,D
30 ProductIDXDPE15284 = 0x8A, // Revision A,B,C,D
31 ProductIDXDPE19283AC = 0x95, // Revision A,B,C
32 ProductIDXDPE19283D = 0xAE, // Revision D
33 ProductIDXDPE192C3AC = 0x96, // Revision A,B,C
34 ProductIDXDPE192C3D = 0xAF, // Revision D
Leo Yangfd204782025-10-30 13:42:02 +080035 ProductIDXDPE192C3E = 0xB8, // Revision E
36 ProductIDXDPE1D2G3B = 0xA5, // Revision B
Christopher Meis7e446a42024-10-22 09:36:41 +020037};
38
Christopher Meisfd341442025-06-16 14:34:51 +020039constexpr uint8_t PMBusICDeviceID = 0xAD;
40constexpr uint8_t PMBusSTLCml = 0x7E;
41constexpr uint8_t IFXICDeviceIDLen = 2;
42constexpr uint8_t IFXMFRAHBAddr = 0xCE;
43constexpr uint8_t IFXMFRRegWrite = 0xDE;
44constexpr uint8_t IFXMFRFwCmdData = 0xFD;
45constexpr uint8_t IFXMFRFwCmd = 0xFE;
Christopher Meisfd341442025-06-16 14:34:51 +020046constexpr uint8_t MFRFwCmdRmng = 0x10;
47constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E;
48constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11;
49constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12;
50constexpr uint8_t MFRFwCmdGetCRC = 0x2D;
Daniel Hsu7e0ecac2025-06-27 11:02:11 +080051constexpr int XDPE152XXConfSize = 1344;
52constexpr int XDPE152XXDConfSize = 1312;
53constexpr int XDPE192XXBConfSize = 1416; // Config(728) + PMBus(568) + SVID(120)
Leo Yangfd204782025-10-30 13:42:02 +080054constexpr int XDPE192C3EConfSize = 1532; // Config(844) + PMBus(568) + SVID(120)
55constexpr int XDPE1D2G3BConfSize = 1552; // Config(864) + PMBus(568) + SVID(120)
Christopher Meisfd341442025-06-16 14:34:51 +020056constexpr uint8_t VRWarnRemaining = 3;
57constexpr uint8_t SectTrim = 0x02;
58
59constexpr uint16_t MFRDefaultWaitTime = 20;
60constexpr uint16_t MFRGetHWAddressWaitTime = 5;
61constexpr uint16_t MFROTPFileInvalidationWaitTime = 100;
62constexpr uint16_t MFRSectionInvalidationWaitTime = 4;
Christopher Meisfd341442025-06-16 14:34:51 +020063
64constexpr uint32_t CRC32Poly = 0xEDB88320;
Christopher Meis7e446a42024-10-22 09:36:41 +020065
66const char* const AddressField = "PMBus Address :";
67const char* const ChecksumField = "Checksum :";
Christopher Meisfd341442025-06-16 14:34:51 +020068const char* const DataStartTag = "[Configuration Data]";
Christopher Meis7e446a42024-10-22 09:36:41 +020069const char* const DataEndTag = "[End Configuration Data]";
70const char* const DataComment = "//";
71const char* const DataXV = "XV";
72
73XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
74 uint16_t address) :
75 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
76{}
77
Christopher Meis7e446a42024-10-22 09:36:41 +020078sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
Christopher Meis7e446a42024-10-22 09:36:41 +020079{
Christopher Meis7e446a42024-10-22 09:36:41 +020080 uint8_t tbuf[16] = {0};
81 tbuf[0] = PMBusICDeviceID;
82 tbuf[1] = 2;
83 uint8_t tSize = 1;
84 uint8_t rbuf[16] = {0};
85 uint8_t rSize = IFXICDeviceIDLen + 1;
86
Christopher Meisfd341442025-06-16 14:34:51 +020087 if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +020088 {
89 error("Failed to get device ID");
90 co_return false;
91 }
92
Christopher Meisfd341442025-06-16 14:34:51 +020093 // According to datasheet:
94 // rbuf[1]: device revision
95 // rbuf[2]: device id
Christopher Meis7e446a42024-10-22 09:36:41 +020096 std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
Christopher Meisfd341442025-06-16 14:34:51 +020097 info.deviceRev = deviceID[0];
98 info.deviceId = deviceID[1];
99 debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
100 debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
Christopher Meis7e446a42024-10-22 09:36:41 +0200101
102 co_return true;
103}
104
Christopher Meisfd341442025-06-16 14:34:51 +0200105sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
106 uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
Christopher Meis7e446a42024-10-22 09:36:41 +0200107{
Christopher Meis7e446a42024-10-22 09:36:41 +0200108 uint8_t tBuf[16] = {0};
109 uint8_t rBuf[16] = {0};
110 uint8_t tSize = 0;
111 uint8_t rSize = 0;
112
113 if (data)
114 {
115 tBuf[0] = IFXMFRFwCmdData;
116 tBuf[1] = 4; // Block write 4 bytes
117 tSize = 6;
118 std::memcpy(&tBuf[2], data, 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200119 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
120 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200121 {
122 error("Failed to send MFR command: {CMD}", "CMD",
123 std::string("IFXMFRFwCmdDAta"));
124 co_return false;
125 }
126 }
127
128 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
129
130 tBuf[0] = IFXMFRFwCmd;
131 tBuf[1] = cmd;
132 tSize = 2;
133 rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200134 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200135 {
136 error("Failed to send MFR command: {CMD}", "CMD",
137 std::string("IFXMFRFwCmd"));
138 co_return false;
139 }
140
Christopher Meisfd341442025-06-16 14:34:51 +0200141 co_await sdbusplus::async::sleep_for(
142 ctx, std::chrono::milliseconds(processTime));
Christopher Meis7e446a42024-10-22 09:36:41 +0200143
144 if (resp)
145 {
146 tBuf[0] = IFXMFRFwCmdData;
147 tSize = 1;
148 rSize = 6;
Christopher Meisfd341442025-06-16 14:34:51 +0200149 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
150 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200151 {
152 error("Failed to send MFR command: {CMD}", "CMD",
153 std::string("IFXMFRFwCmdData"));
154 co_return false;
155 }
156 if (rBuf[0] != 4)
157 {
158 error(
159 "Failed to receive MFR response with unexpected response size");
160 co_return false;
161 }
162 std::memcpy(resp, rBuf + 1, 4);
163 }
164
165 co_return true;
166}
167
Christopher Meis7e446a42024-10-22 09:36:41 +0200168sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
Christopher Meis7e446a42024-10-22 09:36:41 +0200169{
Christopher Meisfd341442025-06-16 14:34:51 +0200170 // According to datasheet:
171 // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
Christopher Meis7e446a42024-10-22 09:36:41 +0200172 uint8_t tBuf[16] = {0};
173 uint8_t rBuf[16] = {0};
174 uint8_t devId[2] = {0};
175
Christopher Meisfd341442025-06-16 14:34:51 +0200176 if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
177 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200178 {
179 error("Failed to request remaining writes");
180 co_return false;
181 }
182
Christopher Meisfd341442025-06-16 14:34:51 +0200183 if (!(co_await this->getDeviceId(devId)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200184 {
185 error("Failed to request device ID for remaining writes");
186 co_return false;
187 }
188
189 int configSize = getConfigSize(devId[1], devId[0]);
190 if (configSize < 0)
191 {
192 error("Failed to request valid configuration size");
193 co_return false;
194 }
195
196 *remain = REMAINING_TIMES(rBuf, configSize);
197
Christopher Meisfd341442025-06-16 14:34:51 +0200198 co_return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200199}
200
201int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
202{
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800203 static const std::map<std::pair<uint8_t, uint8_t>, int> configSizeMap = {
204 {{ProductIDXDPE19283AC, REV_B}, XDPE192XXBConfSize},
205 {{ProductIDXDPE15284, REV_A}, XDPE152XXConfSize},
206 {{ProductIDXDPE15284, REV_B}, XDPE152XXConfSize},
207 {{ProductIDXDPE15284, REV_C}, XDPE152XXConfSize},
208 {{ProductIDXDPE15284, REV_D}, XDPE152XXDConfSize},
209 {{ProductIDXDPE192C3AC, REV_B}, XDPE192XXBConfSize},
Leo Yangfd204782025-10-30 13:42:02 +0800210 {{ProductIDXDPE192C3E, REV_E}, XDPE192C3EConfSize},
211 {{ProductIDXDPE1D2G3B, REV_B}, XDPE1D2G3BConfSize},
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800212 };
Christopher Meis7e446a42024-10-22 09:36:41 +0200213
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800214 auto it = configSizeMap.find({deviceId, revision});
215 if (it != configSizeMap.end())
Christopher Meis7e446a42024-10-22 09:36:41 +0200216 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800217 return it->second;
Christopher Meis7e446a42024-10-22 09:36:41 +0200218 }
219
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800220 error("Failed to get configuration size of {DEVID} with revision {REV}",
221 "DEVID", deviceId, "REV", revision);
222 return -1;
Christopher Meis7e446a42024-10-22 09:36:41 +0200223}
224
Christopher Meis7e446a42024-10-22 09:36:41 +0200225sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
Christopher Meis7e446a42024-10-22 09:36:41 +0200226{
227 uint8_t tBuf[16] = {0};
228 uint8_t rBuf[16] = {0};
229
Christopher Meisfd341442025-06-16 14:34:51 +0200230 if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
231 rBuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200232 {
233 error("Failed to get CRC value");
234 co_return false;
235 }
236
237 *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
238 (static_cast<uint32_t>(rBuf[2]) << 16) |
239 (static_cast<uint32_t>(rBuf[1]) << 8) |
240 (static_cast<uint32_t>(rBuf[0]));
241
242 co_return true;
243}
244
Christopher Meis7e446a42024-10-22 09:36:41 +0200245sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
Christopher Meis7e446a42024-10-22 09:36:41 +0200246{
Christopher Meis7e446a42024-10-22 09:36:41 +0200247 uint8_t tBuf[16] = {0};
248 uint8_t rBuf[16] = {0};
249 uint8_t remain = 0;
250 uint32_t sum = 0;
251 int size = 0;
252
253 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200254 if (!(co_await getCRC(&sum)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200255 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200256 {
257 error("Failed to program the VR");
258 co_return -1;
259 }
260
Christopher Meisfd341442025-06-16 14:34:51 +0200261 debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
262 debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
263
Christopher Meis7e446a42024-10-22 09:36:41 +0200264 if (!force && (sum == configuration.sumExp))
265 {
266 error("Failed to program the VR - CRC value are equal with no force");
267 co_return -1;
268 }
269
270 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
Christopher Meisfd341442025-06-16 14:34:51 +0200271 if (!(co_await this->getRemainingWrites(&remain)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200272 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
Christopher Meis7e446a42024-10-22 09:36:41 +0200273 {
274 error("Failed to program the VR - unable to obtain remaing writes");
275 co_return -1;
276 }
277
Christopher Meisfd341442025-06-16 14:34:51 +0200278 debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
279
Christopher Meis7e446a42024-10-22 09:36:41 +0200280 if (!remain)
281 {
282 error("Failed to program the VR - no remaining write cycles left");
283 co_return -1;
284 }
285
286 if (!force && (remain <= VRWarnRemaining))
287 {
288 error(
289 "Failed to program the VR - {REMAIN} remaining writes left and not force",
290 "REMAIN", remain);
291 co_return -1;
292 }
293
294 // Added reprogramming of the entire configuration file.
295 // Except for the trim section, all other data will be replaced.
296 // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
297 // and XVcode. If the old sections are not invalidated in OTP, they can
298 // affect the CRC calculation.
Christopher Meisfd341442025-06-16 14:34:51 +0200299 debug("Invalidate current Configuration");
Christopher Meis7e446a42024-10-22 09:36:41 +0200300
301 tBuf[0] = 0xfe;
302 tBuf[1] = 0xfe;
303 tBuf[2] = 0x00;
304 tBuf[3] = 0x00;
305
Christopher Meisfd341442025-06-16 14:34:51 +0200306 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
307 MFROTPFileInvalidationWaitTime, tBuf, NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200308 {
Christopher Meisfd341442025-06-16 14:34:51 +0200309 error("Failed to program the VR - Invalidation of current FW");
310 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200311 }
312
Christopher Meis7e446a42024-10-22 09:36:41 +0200313 for (int i = 0; i < configuration.sectCnt; i++)
314 {
Christopher Meisfd341442025-06-16 14:34:51 +0200315 debug("Programming section: {SEC}", "SEC", i);
Christopher Meis7e446a42024-10-22 09:36:41 +0200316 struct configSect* sect = &configuration.section[i];
317 if (sect == NULL)
318 {
319 error(
320 "Failed to program the VR - unexpected NULL section in config");
Christopher Meisfd341442025-06-16 14:34:51 +0200321 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200322 }
323
324 if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
325 {
Christopher Meisfd341442025-06-16 14:34:51 +0200326 debug("Section Type: {TYPE}", "TYPE", lg2::hex,
327 configuration.section[i].type);
328
329 // clear bit0 of PMBUS_STS_CML
Christopher Meis7e446a42024-10-22 09:36:41 +0200330 tBuf[0] = PMBusSTLCml;
331 tBuf[1] = 0x1;
332 uint8_t tSize = 2;
333 uint8_t rSize = 0;
Christopher Meisfd341442025-06-16 14:34:51 +0200334 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
335 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200336 {
337 error("Failed to program the VR on sendReceive {CMD}", "CMD",
338 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200339 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200340 }
341
Christopher Meisfd341442025-06-16 14:34:51 +0200342 debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
Christopher Meis7e446a42024-10-22 09:36:41 +0200343 tBuf[0] = sect->type;
344 tBuf[1] = 0x00;
345 tBuf[2] = 0x00;
346 tBuf[3] = 0x00;
347
Christopher Meisfd341442025-06-16 14:34:51 +0200348 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
349 MFRSectionInvalidationWaitTime, tBuf,
350 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200351 {
352 error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
353 std::string("MFRFwCmdOTPFileInvd"));
Christopher Meisfd341442025-06-16 14:34:51 +0200354 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200355 }
356
Christopher Meisfd341442025-06-16 14:34:51 +0200357 // set scratchpad addr
358 // XDPE192XX Rev A/B: 0x2005e000
359 // Rev C : 0x2005e400
360 // Rev D : 0x2005f000
361
362 debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
363 info.scratchPadAddress);
Christopher Meis7e446a42024-10-22 09:36:41 +0200364
365 tBuf[0] = IFXMFRAHBAddr;
366 tBuf[1] = 4;
Christopher Meisfd341442025-06-16 14:34:51 +0200367 tBuf[2] = (info.scratchPadAddress) & 0xFF;
368 tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
369 tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
370 tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
Christopher Meis7e446a42024-10-22 09:36:41 +0200371 tSize = 6;
372 rSize = 0;
373
Christopher Meisfd341442025-06-16 14:34:51 +0200374 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
375 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200376 {
377 error("Failed to program VR on sendReceive on {CMD}", "CMD",
378 std::string("IFXMFRAHBAddr"));
Christopher Meisfd341442025-06-16 14:34:51 +0200379 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200380 }
381
382 co_await sdbusplus::async::sleep_for(
383 ctx, std::chrono::microseconds(10000));
384 size = 0;
385 }
386
387 // programm into scratchpad
388 for (int j = 0; j < sect->dataCnt; j++)
389 {
390 tBuf[0] = IFXMFRRegWrite;
391 tBuf[1] = 4;
392 uint8_t tSize = 6;
393 uint8_t rSize = 0;
394 memcpy(&tBuf[2], &sect->data[j], 4);
Christopher Meisfd341442025-06-16 14:34:51 +0200395 if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
396 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200397 {
398 error("Failed to program the VR on sendReceive {CMD}", "CMD",
399 std::string("IFXMFRRegWrite"));
Christopher Meisfd341442025-06-16 14:34:51 +0200400 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200401 }
Christopher Meisfd341442025-06-16 14:34:51 +0200402 co_await sdbusplus::async::sleep_for(ctx,
403 std::chrono::milliseconds(10));
Christopher Meis7e446a42024-10-22 09:36:41 +0200404 }
405
406 size += sect->dataCnt * 4;
407 if ((i + 1 >= configuration.sectCnt) ||
408 (sect->type != configuration.section[i + 1].type))
409 {
Christopher Meisfd341442025-06-16 14:34:51 +0200410 // wait for programming soak (2ms/byte, at least 200ms)
411 // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
412 uint16_t soakTime = 100 * ((size / 50) + 2);
413
Christopher Meis7e446a42024-10-22 09:36:41 +0200414 // Upload to scratchpad
Christopher Meisfd341442025-06-16 14:34:51 +0200415 debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
416 "TIME", soakTime);
Christopher Meis7e446a42024-10-22 09:36:41 +0200417 std::memcpy(tBuf, &size, 2);
418 tBuf[2] = 0x00;
419 tBuf[3] = 0x00;
Christopher Meisfd341442025-06-16 14:34:51 +0200420 if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
421 NULL)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200422 {
423 error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
424 std::string("MFRFwCmdOTPConfSTO"));
Christopher Meisfd341442025-06-16 14:34:51 +0200425 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200426 }
427
Christopher Meisfd341442025-06-16 14:34:51 +0200428 // Read status faults after programming
Christopher Meis7e446a42024-10-22 09:36:41 +0200429 tBuf[0] = PMBusSTLCml;
430 uint8_t tSize = 1;
431 uint8_t rSize = 1;
Christopher Meisfd341442025-06-16 14:34:51 +0200432 if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
433 rSize)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200434 {
435 error("Failed to program VR on sendReceive {CMD}", "CMD",
436 std::string("PMBusSTLCml"));
Christopher Meisfd341442025-06-16 14:34:51 +0200437 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200438 }
439 if (rBuf[0] & 0x01)
440 {
Christopher Meisfd341442025-06-16 14:34:51 +0200441 error("Failed to program VR - status fault indicated error");
442 co_return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200443 }
444 }
445 }
446
Christopher Meis7e446a42024-10-22 09:36:41 +0200447 co_return true;
448}
449
450int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
451{
452 char* s = strtok(src, delim);
453 int size = 0;
454 int maxSz = 5;
455
456 while (s)
457 {
458 *dest++ = s;
459 if ((++size) >= maxSz)
460 {
461 break;
462 }
463 s = strtok(NULL, delim);
464 }
465
466 return size;
467}
468
Christopher Meisfd341442025-06-16 14:34:51 +0200469bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
Christopher Meis7e446a42024-10-22 09:36:41 +0200470{
471 size_t lenEndTag = strlen(DataEndTag);
472 size_t lenStartTag = strlen(DataStartTag);
473 size_t lenComment = strlen(DataComment);
474 size_t lenXV = strlen(DataXV);
475 size_t start = 0;
476 const int maxLineLength = 40;
477 char line[maxLineLength];
478 char* token = NULL;
479 bool isData = false;
480 char delim = ' ';
481 uint16_t offset;
482 uint8_t sectType = 0x0;
483 uint32_t dWord;
484 int dataCnt = 0;
485 int sectIndex = -1;
486
487 for (size_t i = 0; i < image_size; i++)
488 {
489 if (image[i] == '\n')
490 {
Leo Yangd9b99a92025-11-03 14:16:16 +0800491 size_t lineLength = i - start;
492 if (i > start && image[i - 1] == '\r')
493 {
494 lineLength--;
495 }
Leo Yangecee4a62025-10-30 15:44:38 +0800496 if (lineLength >= maxLineLength)
497 {
Leo Yangd9b99a92025-11-03 14:16:16 +0800498 error("line length >= 40, please check image file.");
Leo Yangecee4a62025-10-30 15:44:38 +0800499 return false;
500 }
501 std::memcpy(line, image + start, lineLength);
502 line[lineLength] = '\0';
503
Christopher Meis7e446a42024-10-22 09:36:41 +0200504 if (!strncmp(line, DataComment, lenComment))
505 {
506 token = line + lenComment;
507 if (!strncmp(token, DataXV, lenXV))
508 {
509 debug("Parsing: {OBJ}", "OBJ",
510 reinterpret_cast<const char*>(line));
511 }
512 start = i + 1;
513 continue;
514 }
515 if (!strncmp(line, DataEndTag, lenEndTag))
516 {
517 debug("Parsing: {OBJ}", "OBJ",
518 reinterpret_cast<const char*>(line));
519 break;
520 }
521 else if (isData)
522 {
523 char* tokenList[8] = {0};
524 int tokenSize = lineSplit(tokenList, line, &delim);
525 if (tokenSize < 1)
526 {
527 start = i + 1;
528 continue;
529 }
530
531 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
532 if (sectType == SectTrim && offset != 0x0)
533 {
534 continue;
535 }
536
537 for (int i = 1; i < tokenSize; i++)
538 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800539 dWord = (uint32_t)strtoul(tokenList[i], NULL, 16);
Christopher Meis7e446a42024-10-22 09:36:41 +0200540 if ((offset == 0x0) && (i == 1))
541 {
542 sectType = (uint8_t)dWord;
543 if (sectType == SectTrim)
544 {
545 break;
546 }
547 if ((++sectIndex) >= MaxSectCnt)
548 {
Christopher Meisfd341442025-06-16 14:34:51 +0200549 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200550 }
551
552 configuration.section[sectIndex].type = sectType;
553 configuration.sectCnt = sectIndex + 1;
554 dataCnt = 0;
555 }
556
557 if (dataCnt >= MaxSectDataCnt)
558 {
Christopher Meisfd341442025-06-16 14:34:51 +0200559 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200560 }
561
562 configuration.section[sectIndex].data[dataCnt++] = dWord;
563 configuration.section[sectIndex].dataCnt = dataCnt;
564 configuration.totalCnt++;
565 }
566 }
567 else
568 {
569 if ((token = strstr(line, AddressField)) != NULL)
570 {
571 if ((token = strstr(token, "0x")) != NULL)
572 {
573 configuration.addr =
574 (uint8_t)(strtoul(token, NULL, 16) << 1);
575 }
576 }
577 else if ((token = strstr(line, ChecksumField)) != NULL)
578 {
579 if ((token = strstr(token, "0x")) != NULL)
580 {
581 configuration.sumExp =
582 (uint32_t)strtoul(token, NULL, 16);
583 }
584 }
585 else if (!strncmp(line, DataStartTag, lenStartTag))
586 {
587 isData = true;
588 start = i + 1;
589 continue;
590 }
591 else
592 {
593 start = i + 1;
594 continue;
595 }
596 }
597 start = i + 1;
598 }
599 }
600
Christopher Meisfd341442025-06-16 14:34:51 +0200601 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200602}
603
Christopher Meisfd341442025-06-16 14:34:51 +0200604bool XDPE1X2XX::checkImage()
Christopher Meis7e446a42024-10-22 09:36:41 +0200605{
606 uint8_t i;
607 uint32_t crc;
608 uint32_t sum = 0;
609
610 for (i = 0; i < configuration.sectCnt; i++)
611 {
612 struct configSect* sect = &configuration.section[i];
613 if (sect == NULL)
614 {
615 error("Failed to check image - unexpected NULL section");
Christopher Meisfd341442025-06-16 14:34:51 +0200616 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200617 }
618
Christopher Meisfd341442025-06-16 14:34:51 +0200619 crc = calcCRC32(&sect->data[0], 2);
Christopher Meis7e446a42024-10-22 09:36:41 +0200620 if (crc != sect->data[2])
621 {
622 error("Failed to check image - first CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200623 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200624 }
625 sum += crc;
626
627 // check CRC of section data
628 crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
629 if (crc != sect->data[sect->dataCnt - 1])
630 {
631 error("Failed to check image - second CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200632 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200633 }
634 sum += crc;
635 }
636
637 if (sum != configuration.sumExp)
638 {
Christopher Meisfd341442025-06-16 14:34:51 +0200639 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
640 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
Christopher Meis7e446a42024-10-22 09:36:41 +0200641 error("Failed to check image - third CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200642 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200643 }
644
Christopher Meisfd341442025-06-16 14:34:51 +0200645 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200646}
647
Christopher Meis7e446a42024-10-22 09:36:41 +0200648sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
649 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200650{
Christopher Meisfd341442025-06-16 14:34:51 +0200651 if (!parseImage(image, imageSize))
Christopher Meis7e446a42024-10-22 09:36:41 +0200652 {
653 error("Failed to update firmware on parsing Image");
654 co_return false;
655 }
656
Christopher Meisfd341442025-06-16 14:34:51 +0200657 if (!checkImage())
Christopher Meis7e446a42024-10-22 09:36:41 +0200658 {
659 error("Failed to update firmware on check image");
660 co_return false;
661 }
662
663 co_return true;
664}
665
Christopher Meisfd341442025-06-16 14:34:51 +0200666sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
Christopher Meis7e446a42024-10-22 09:36:41 +0200667{
Christopher Meisfd341442025-06-16 14:34:51 +0200668 uint8_t tbuf[16] = {0};
669 uint8_t rbuf[16] = {0};
670
671 tbuf[0] = 0x02;
672 tbuf[1] = 0x00;
673 tbuf[2] = 0x00;
674 tbuf[3] = 0x00;
675
676 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
677 rbuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200678 {
Christopher Meisfd341442025-06-16 14:34:51 +0200679 error("mfrFWcmd call failed to retrieve scratchpad address");
Christopher Meis7e446a42024-10-22 09:36:41 +0200680 co_return false;
681 }
682
Christopher Meisfd341442025-06-16 14:34:51 +0200683 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
684 (static_cast<uint32_t>(rbuf[2]) << 16) |
685 (static_cast<uint32_t>(rbuf[1]) << 8) |
686 (static_cast<uint32_t>(rbuf[0]));
687
688 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
689 info.scratchPadAddress);
690
691 co_return true;
692}
693
694sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
695{
696 bool ret = true;
697 if (!(co_await getScratchPadAddress()))
698 {
699 error("Failed to retrieve scratchpad address");
700 co_return false;
701 }
702
703 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
704 ret = co_await program(force);
705 if (!ret)
706 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
707 {
708 error("Failed to update firmware on program");
709 }
710
711 info.deviceId = 0;
712 info.deviceRev = 0;
713 info.remainingWrites = 0;
714 info.scratchPadAddress = 0;
715 info.actualCRC = 0;
716 info.configSize = 0;
717
Christopher Meis7e446a42024-10-22 09:36:41 +0200718 // Reset the configuration
719 configuration.addr = 0;
720 configuration.totalCnt = 0;
721 configuration.sumExp = 0;
722 configuration.sectCnt = 0;
723 for (int i = 0; i <= MaxSectCnt - 1; i++)
724 {
725 configuration.section[i].type = 0;
726 configuration.section[i].dataCnt = 0;
Leo Yangecee4a62025-10-30 15:44:38 +0800727 for (int j = 0; j < MaxSectDataCnt; j++)
Christopher Meis7e446a42024-10-22 09:36:41 +0200728 {
729 configuration.section[i].data[j] = 0;
730 }
731 }
732
Christopher Meisfd341442025-06-16 14:34:51 +0200733 if (!ret)
734 {
735 co_return false;
736 }
737
Christopher Meis7e446a42024-10-22 09:36:41 +0200738 co_return true;
739}
740
Christopher Meis7e446a42024-10-22 09:36:41 +0200741uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
742{
743 if (data == NULL)
744 {
745 return 0;
746 }
747
748 uint32_t crc = 0xFFFFFFFF;
749 for (int i = 0; i < len; i++)
750 {
751 crc ^= data[i];
752
753 for (int b = 0; b < 32; b++)
754 {
755 if (crc & 0x1)
756 {
757 crc = (crc >> 1) ^ CRC32Poly; // lsb-first
758 }
759 else
760 {
761 crc >>= 1;
762 }
763 }
764 }
765
766 return ~crc;
767}
768
769bool XDPE1X2XX::forcedUpdateAllowed()
770{
771 return true;
772}
773
774} // namespace phosphor::software::VR