blob: 1247bbe526c4e9bc588f3502f91e379c21f666bc [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 Yangecee4a62025-10-30 15:44:38 +0800491 const size_t lineLength = i - start;
492 if (lineLength >= maxLineLength)
493 {
494 error("line length exceeded 40, please check image file.");
495 return false;
496 }
497 std::memcpy(line, image + start, lineLength);
498 line[lineLength] = '\0';
499
Christopher Meis7e446a42024-10-22 09:36:41 +0200500 if (!strncmp(line, DataComment, lenComment))
501 {
502 token = line + lenComment;
503 if (!strncmp(token, DataXV, lenXV))
504 {
505 debug("Parsing: {OBJ}", "OBJ",
506 reinterpret_cast<const char*>(line));
507 }
508 start = i + 1;
509 continue;
510 }
511 if (!strncmp(line, DataEndTag, lenEndTag))
512 {
513 debug("Parsing: {OBJ}", "OBJ",
514 reinterpret_cast<const char*>(line));
515 break;
516 }
517 else if (isData)
518 {
519 char* tokenList[8] = {0};
520 int tokenSize = lineSplit(tokenList, line, &delim);
521 if (tokenSize < 1)
522 {
523 start = i + 1;
524 continue;
525 }
526
527 offset = (uint16_t)strtol(tokenList[0], NULL, 16);
528 if (sectType == SectTrim && offset != 0x0)
529 {
530 continue;
531 }
532
533 for (int i = 1; i < tokenSize; i++)
534 {
Daniel Hsu7e0ecac2025-06-27 11:02:11 +0800535 dWord = (uint32_t)strtoul(tokenList[i], NULL, 16);
Christopher Meis7e446a42024-10-22 09:36:41 +0200536 if ((offset == 0x0) && (i == 1))
537 {
538 sectType = (uint8_t)dWord;
539 if (sectType == SectTrim)
540 {
541 break;
542 }
543 if ((++sectIndex) >= MaxSectCnt)
544 {
Christopher Meisfd341442025-06-16 14:34:51 +0200545 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200546 }
547
548 configuration.section[sectIndex].type = sectType;
549 configuration.sectCnt = sectIndex + 1;
550 dataCnt = 0;
551 }
552
553 if (dataCnt >= MaxSectDataCnt)
554 {
Christopher Meisfd341442025-06-16 14:34:51 +0200555 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200556 }
557
558 configuration.section[sectIndex].data[dataCnt++] = dWord;
559 configuration.section[sectIndex].dataCnt = dataCnt;
560 configuration.totalCnt++;
561 }
562 }
563 else
564 {
565 if ((token = strstr(line, AddressField)) != NULL)
566 {
567 if ((token = strstr(token, "0x")) != NULL)
568 {
569 configuration.addr =
570 (uint8_t)(strtoul(token, NULL, 16) << 1);
571 }
572 }
573 else if ((token = strstr(line, ChecksumField)) != NULL)
574 {
575 if ((token = strstr(token, "0x")) != NULL)
576 {
577 configuration.sumExp =
578 (uint32_t)strtoul(token, NULL, 16);
579 }
580 }
581 else if (!strncmp(line, DataStartTag, lenStartTag))
582 {
583 isData = true;
584 start = i + 1;
585 continue;
586 }
587 else
588 {
589 start = i + 1;
590 continue;
591 }
592 }
593 start = i + 1;
594 }
595 }
596
Christopher Meisfd341442025-06-16 14:34:51 +0200597 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200598}
599
Christopher Meisfd341442025-06-16 14:34:51 +0200600bool XDPE1X2XX::checkImage()
Christopher Meis7e446a42024-10-22 09:36:41 +0200601{
602 uint8_t i;
603 uint32_t crc;
604 uint32_t sum = 0;
605
606 for (i = 0; i < configuration.sectCnt; i++)
607 {
608 struct configSect* sect = &configuration.section[i];
609 if (sect == NULL)
610 {
611 error("Failed to check image - unexpected NULL section");
Christopher Meisfd341442025-06-16 14:34:51 +0200612 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200613 }
614
Christopher Meisfd341442025-06-16 14:34:51 +0200615 crc = calcCRC32(&sect->data[0], 2);
Christopher Meis7e446a42024-10-22 09:36:41 +0200616 if (crc != sect->data[2])
617 {
618 error("Failed to check image - first CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200619 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200620 }
621 sum += crc;
622
623 // check CRC of section data
624 crc = calcCRC32(&sect->data[3], sect->dataCnt - 4);
625 if (crc != sect->data[sect->dataCnt - 1])
626 {
627 error("Failed to check image - second CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200628 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200629 }
630 sum += crc;
631 }
632
633 if (sum != configuration.sumExp)
634 {
Christopher Meisfd341442025-06-16 14:34:51 +0200635 debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
636 debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
Christopher Meis7e446a42024-10-22 09:36:41 +0200637 error("Failed to check image - third CRC value mismatch");
Christopher Meisfd341442025-06-16 14:34:51 +0200638 return false;
Christopher Meis7e446a42024-10-22 09:36:41 +0200639 }
640
Christopher Meisfd341442025-06-16 14:34:51 +0200641 return true;
Christopher Meis7e446a42024-10-22 09:36:41 +0200642}
643
Christopher Meis7e446a42024-10-22 09:36:41 +0200644sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
645 size_t imageSize)
Christopher Meis7e446a42024-10-22 09:36:41 +0200646{
Christopher Meisfd341442025-06-16 14:34:51 +0200647 if (!parseImage(image, imageSize))
Christopher Meis7e446a42024-10-22 09:36:41 +0200648 {
649 error("Failed to update firmware on parsing Image");
650 co_return false;
651 }
652
Christopher Meisfd341442025-06-16 14:34:51 +0200653 if (!checkImage())
Christopher Meis7e446a42024-10-22 09:36:41 +0200654 {
655 error("Failed to update firmware on check image");
656 co_return false;
657 }
658
659 co_return true;
660}
661
Christopher Meisfd341442025-06-16 14:34:51 +0200662sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
Christopher Meis7e446a42024-10-22 09:36:41 +0200663{
Christopher Meisfd341442025-06-16 14:34:51 +0200664 uint8_t tbuf[16] = {0};
665 uint8_t rbuf[16] = {0};
666
667 tbuf[0] = 0x02;
668 tbuf[1] = 0x00;
669 tbuf[2] = 0x00;
670 tbuf[3] = 0x00;
671
672 if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
673 rbuf)))
Christopher Meis7e446a42024-10-22 09:36:41 +0200674 {
Christopher Meisfd341442025-06-16 14:34:51 +0200675 error("mfrFWcmd call failed to retrieve scratchpad address");
Christopher Meis7e446a42024-10-22 09:36:41 +0200676 co_return false;
677 }
678
Christopher Meisfd341442025-06-16 14:34:51 +0200679 info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
680 (static_cast<uint32_t>(rbuf[2]) << 16) |
681 (static_cast<uint32_t>(rbuf[1]) << 8) |
682 (static_cast<uint32_t>(rbuf[0]));
683
684 debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
685 info.scratchPadAddress);
686
687 co_return true;
688}
689
690sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
691{
692 bool ret = true;
693 if (!(co_await getScratchPadAddress()))
694 {
695 error("Failed to retrieve scratchpad address");
696 co_return false;
697 }
698
699 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
700 ret = co_await program(force);
701 if (!ret)
702 // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
703 {
704 error("Failed to update firmware on program");
705 }
706
707 info.deviceId = 0;
708 info.deviceRev = 0;
709 info.remainingWrites = 0;
710 info.scratchPadAddress = 0;
711 info.actualCRC = 0;
712 info.configSize = 0;
713
Christopher Meis7e446a42024-10-22 09:36:41 +0200714 // Reset the configuration
715 configuration.addr = 0;
716 configuration.totalCnt = 0;
717 configuration.sumExp = 0;
718 configuration.sectCnt = 0;
719 for (int i = 0; i <= MaxSectCnt - 1; i++)
720 {
721 configuration.section[i].type = 0;
722 configuration.section[i].dataCnt = 0;
Leo Yangecee4a62025-10-30 15:44:38 +0800723 for (int j = 0; j < MaxSectDataCnt; j++)
Christopher Meis7e446a42024-10-22 09:36:41 +0200724 {
725 configuration.section[i].data[j] = 0;
726 }
727 }
728
Christopher Meisfd341442025-06-16 14:34:51 +0200729 if (!ret)
730 {
731 co_return false;
732 }
733
Christopher Meis7e446a42024-10-22 09:36:41 +0200734 co_return true;
735}
736
Christopher Meis7e446a42024-10-22 09:36:41 +0200737uint32_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