blob: 2fb7c8676a10f9e88a3bf666698847acd7742ad1 [file] [log] [blame]
Daniel Hsuf6470b52025-02-26 15:03:47 +08001#include "lattice.hpp"
2
3#include <phosphor-logging/lg2.hpp>
4
5#include <algorithm>
Potin Lai7b925762025-08-18 16:20:29 +08006#include <cstddef>
Daniel Hsuf6470b52025-02-26 15:03:47 +08007#include <fstream>
8#include <map>
Ken Chend9825682025-07-09 13:49:48 +08009#include <numeric>
Daniel Hsuf6470b52025-02-26 15:03:47 +080010#include <thread>
11#include <vector>
12
Daniel Hsuf6470b52025-02-26 15:03:47 +080013constexpr uint8_t busyWaitmaxRetry = 30;
14constexpr uint8_t busyFlagBit = 0x80;
15constexpr std::chrono::milliseconds waitBusyTime(200);
16
Ken Chen9c05c3c2025-07-24 20:56:34 +080017static constexpr std::string_view tagFuseQuantity = "QF";
18static constexpr std::string_view tagUserCodeHex = "UH";
Daniel Hsuf6470b52025-02-26 15:03:47 +080019static constexpr std::string_view tagCFStart = "L000";
Ken Chend9825682025-07-09 13:49:48 +080020static constexpr std::string_view tagData = "NOTE TAG DATA";
Ken Chen9c05c3c2025-07-24 20:56:34 +080021static constexpr std::string_view tagUserFlashMemory = "NOTE USER MEMORY DATA";
Daniel Hsuf6470b52025-02-26 15:03:47 +080022static constexpr std::string_view tagChecksum = "C";
23static constexpr std::string_view tagUserCode = "NOTE User Electronic";
24static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
Ken Chend9825682025-07-09 13:49:48 +080025static constexpr std::string_view tagEndConfig = "NOTE END CONFIG DATA";
26static constexpr std::string_view tagDevName = "NOTE DEVICE NAME";
Daniel Hsuf6470b52025-02-26 15:03:47 +080027
28constexpr uint8_t isOK = 0;
29constexpr uint8_t isReady = 0;
30constexpr uint8_t busyOrReadyBit = 4;
31constexpr uint8_t failOrOKBit = 5;
32
Daniel Hsuf6470b52025-02-26 15:03:47 +080033enum cpldI2cCmd
34{
35 commandEraseFlash = 0x0E,
36 commandDisableConfigInterface = 0x26,
37 commandReadStatusReg = 0x3C,
38 commandResetConfigFlash = 0x46,
39 commandProgramDone = 0x5E,
40 commandProgramPage = 0x70,
Potin Lai7b925762025-08-18 16:20:29 +080041 commandReadPage = 0x73,
Daniel Hsuf6470b52025-02-26 15:03:47 +080042 commandEnableConfigMode = 0x74,
Potin Lai7b925762025-08-18 16:20:29 +080043 commandSetPageAddress = 0xB4,
Daniel Hsuf6470b52025-02-26 15:03:47 +080044 commandReadFwVersion = 0xC0,
45 commandProgramUserCode = 0xC2,
46 commandReadDeviceId = 0xE0,
47 commandReadBusyFlag = 0xF0,
48};
49
50static uint8_t reverse_bit(uint8_t b)
51{
52 b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
53 b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
54 b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
55 return b;
56}
57
Daniel Hsu37a30142025-06-12 17:57:24 +080058std::string uint32ToHexStr(uint32_t value)
59{
60 std::ostringstream oss;
61 oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
62 << value;
63 return oss.str();
64}
65
Daniel Hsuf6470b52025-02-26 15:03:47 +080066bool CpldLatticeManager::jedFileParser()
67{
Ken Chend9825682025-07-09 13:49:48 +080068 enum class ParseState
69 {
70 none,
71 cfg,
72 endCfg,
73 ufm,
74 checksum,
75 userCode
76 };
77 ParseState state = ParseState::none;
Daniel Hsuf6470b52025-02-26 15:03:47 +080078
79 if (image == nullptr || imageSize == 0)
80 {
81 lg2::error(
82 "Error: JED file is empty or not found. Please check the file.");
83 return false;
84 }
85
Daniel Hsuf6470b52025-02-26 15:03:47 +080086 std::string content(reinterpret_cast<const char*>(image), imageSize);
Daniel Hsuf6470b52025-02-26 15:03:47 +080087 std::istringstream iss(content);
88 std::string line;
89
Ken Chend9825682025-07-09 13:49:48 +080090 auto pushPage = [](std::string& line, std::vector<uint8_t>& sector) {
91 if (line[0] == '0' || line[0] == '1')
92 {
93 while (line.size() >= 8)
94 {
95 try
96 {
97 sector.push_back(static_cast<uint8_t>(
98 std::stoi(line.substr(0, 8), 0, 2)));
99 line.erase(0, 8);
100 }
101 catch (...)
102 {
103 break;
104 }
105 }
106 }
107 };
108
Daniel Hsuf6470b52025-02-26 15:03:47 +0800109 while (getline(iss, line))
110 {
Ken Chend9825682025-07-09 13:49:48 +0800111 if (!line.empty() && line.back() == '\r')
Daniel Hsuf6470b52025-02-26 15:03:47 +0800112 {
Ken Chend9825682025-07-09 13:49:48 +0800113 line.pop_back();
Daniel Hsuf6470b52025-02-26 15:03:47 +0800114 }
Ken Chend9825682025-07-09 13:49:48 +0800115 if (line.empty())
Daniel Hsuf6470b52025-02-26 15:03:47 +0800116 {
Daniel Hsuf6470b52025-02-26 15:03:47 +0800117 continue;
118 }
Daniel Hsuf6470b52025-02-26 15:03:47 +0800119
Ken Chen9c05c3c2025-07-24 20:56:34 +0800120 if (line.starts_with(tagFuseQuantity))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800121 {
Ken Chend9825682025-07-09 13:49:48 +0800122 ssize_t numberSize = static_cast<ssize_t>(line.find('*')) -
123 static_cast<ssize_t>(line.find('F')) - 1;
124 if (numberSize > 0)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800125 {
Ken Chen9c05c3c2025-07-24 20:56:34 +0800126 fwInfo.fuseQuantity = std::stoul(
127 line.substr(tagFuseQuantity.length(), numberSize));
128 lg2::debug("fuseQuantity Size = {QFSIZE}", "QFSIZE",
129 fwInfo.fuseQuantity);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800130 }
Ken Chend9825682025-07-09 13:49:48 +0800131 }
132 else if (line.starts_with(tagCFStart) ||
133 line.starts_with(tagEbrInitData))
134 {
135 state = ParseState::cfg;
136 continue;
137 }
138 else if (line.starts_with(tagEndConfig))
139 {
140 state = ParseState::endCfg;
141 continue;
142 }
Ken Chen9c05c3c2025-07-24 20:56:34 +0800143 else if (line.starts_with(tagUserFlashMemory) ||
144 line.starts_with(tagData))
Ken Chend9825682025-07-09 13:49:48 +0800145 {
146 state = ParseState::ufm;
147 continue;
148 }
149 else if (line.starts_with(tagUserCode))
150 {
151 state = ParseState::userCode;
152 continue;
153 }
154 else if (line.starts_with(tagChecksum))
155 {
156 state = ParseState::checksum;
157 }
158 else if (line.starts_with(tagDevName))
159 {
160 lg2::debug("{DEVNAME}", "DEVNAME", line);
161 if (line.find(chip) == std::string::npos)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800162 {
Ken Chend9825682025-07-09 13:49:48 +0800163 lg2::debug("STOP UPDATING: The image does not match the chip.");
164 return -1;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800165 }
166 }
167
Ken Chend9825682025-07-09 13:49:48 +0800168 switch (state)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800169 {
Ken Chend9825682025-07-09 13:49:48 +0800170 case ParseState::cfg:
171 pushPage(line, fwInfo.cfgData);
172 break;
173 case ParseState::endCfg:
174 pushPage(line, sumOnly);
175 break;
176 case ParseState::ufm:
177 pushPage(line, fwInfo.ufmData);
178 break;
179 case ParseState::checksum:
180 if (line.size() > 1)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800181 {
Ken Chend9825682025-07-09 13:49:48 +0800182 state = ParseState::none;
183 ssize_t numberSize =
184 static_cast<ssize_t>(line.find('*')) -
185 static_cast<ssize_t>(line.find('C')) - 1;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800186 if (numberSize <= 0)
187 {
Ken Chend9825682025-07-09 13:49:48 +0800188 lg2::debug("Error in parsing checksum");
189 return -1;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800190 }
Ken Chend9825682025-07-09 13:49:48 +0800191 static constexpr auto start = tagChecksum.length();
Daniel Hsuf6470b52025-02-26 15:03:47 +0800192 std::istringstream iss(line.substr(start, numberSize));
Ken Chend9825682025-07-09 13:49:48 +0800193 iss >> std::hex >> fwInfo.checksum;
194 lg2::debug("Checksum = 0x{CHECKSUM}", "CHECKSUM",
195 fwInfo.checksum);
196 }
197 break;
198 case ParseState::userCode:
Ken Chen9c05c3c2025-07-24 20:56:34 +0800199 if (line.starts_with(tagUserCodeHex))
Ken Chend9825682025-07-09 13:49:48 +0800200 {
201 state = ParseState::none;
202 ssize_t numberSize =
203 static_cast<ssize_t>(line.find('*')) -
204 static_cast<ssize_t>(line.find('H')) - 1;
205 if (numberSize <= 0)
206 {
207 lg2::debug("Error in parsing usercode");
208 return -1;
209 }
210 std::istringstream iss(
Ken Chen9c05c3c2025-07-24 20:56:34 +0800211 line.substr(tagUserCodeHex.length(), numberSize));
Daniel Hsuf6470b52025-02-26 15:03:47 +0800212 iss >> std::hex >> fwInfo.version;
Ken Chend9825682025-07-09 13:49:48 +0800213 lg2::debug("UserCode = 0x{USERCODE}", "USERCODE",
Daniel Hsuf6470b52025-02-26 15:03:47 +0800214 fwInfo.version);
215 }
Ken Chend9825682025-07-09 13:49:48 +0800216 break;
217 default:
218 break;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800219 }
Ken Chend9825682025-07-09 13:49:48 +0800220 }
221
222 lg2::debug("CFG Size = {CFGSIZE}", "CFGSIZE", fwInfo.cfgData.size());
223 if (!fwInfo.ufmData.empty())
224 {
Ken Chen9c05c3c2025-07-24 20:56:34 +0800225 lg2::debug("userFlashMemory size = {UFMSIZE}", "UFMSIZE",
226 fwInfo.ufmData.size());
Daniel Hsuf6470b52025-02-26 15:03:47 +0800227 }
228
229 return true;
230}
231
232bool CpldLatticeManager::verifyChecksum()
233{
Ken Chend9825682025-07-09 13:49:48 +0800234 uint32_t calculated = 0U;
235 auto addByte = [](uint32_t sum, uint8_t byte) {
236 return sum + reverse_bit(byte);
237 };
Daniel Hsuf6470b52025-02-26 15:03:47 +0800238
Ken Chend9825682025-07-09 13:49:48 +0800239 calculated = std::accumulate(fwInfo.cfgData.begin(), fwInfo.cfgData.end(),
240 calculated, addByte);
241 calculated =
242 std::accumulate(sumOnly.begin(), sumOnly.end(), calculated, addByte);
243 calculated = std::accumulate(fwInfo.ufmData.begin(), fwInfo.ufmData.end(),
244 calculated, addByte);
245
246 lg2::debug("Calculated checksum = {CALCULATED}", "CALCULATED", lg2::hex,
247 calculated);
248 lg2::debug("Checksum from JED file = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
249 lg2::hex, fwInfo.checksum);
250
251 if (fwInfo.checksum != (calculated & 0xFFFF))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800252 {
Ken Chend9825682025-07-09 13:49:48 +0800253 lg2::error("JED file checksum compare fail, "
254 "Calculated checksum = {CALCULATED}, "
255 "Checksum from JED file = {JEDFILECHECKSUM}",
256 "CALCULATED", lg2::hex, calculated, "JEDFILECHECKSUM",
257 lg2::hex, fwInfo.checksum);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800258 return false;
259 }
260
Ken Chend9825682025-07-09 13:49:48 +0800261 lg2::debug("JED file checksum compare success");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800262 return true;
263}
264
265sdbusplus::async::task<bool> CpldLatticeManager::readDeviceId()
266{
Daniel Hsu37a30142025-06-12 17:57:24 +0800267 std::vector<uint8_t> request = {commandReadDeviceId, 0x0, 0x0, 0x0};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800268 constexpr size_t resSize = 4;
269 std::vector<uint8_t> readData(resSize, 0);
Daniel Hsu37a30142025-06-12 17:57:24 +0800270 bool success = co_await i2cInterface.sendReceive(
271 request.data(), request.size(), readData.data(), resSize);
272
Daniel Hsuf6470b52025-02-26 15:03:47 +0800273 if (!success)
274 {
275 lg2::error(
276 "Fail to read device Id. Please check the I2C bus and address.");
277 co_return false;
278 }
279
Daniel Hsu61e12672025-06-12 19:20:46 +0800280 auto chipWantToUpdate =
281 std::find_if(supportedDeviceMap.begin(), supportedDeviceMap.end(),
282 [this](const auto& pair) {
283 return pair.second.chipName == this->chip;
284 });
Daniel Hsuf6470b52025-02-26 15:03:47 +0800285
Daniel Hsu61e12672025-06-12 19:20:46 +0800286 if (chipWantToUpdate != supportedDeviceMap.end() &&
287 chipWantToUpdate->second.deviceId == readData)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800288 {
289 if (chip.rfind("LCMXO3D", 0) == 0)
290 {
291 isLCMXO3D = true;
292 if (!target.empty() && target != "CFG0" && target != "CFG1")
293 {
294 lg2::error("Unknown target. Only CFG0 and CFG1 are supported.");
295 co_return false;
296 }
297 }
298
299 lg2::debug("Device ID match with chip. Chip name: {CHIPNAME}",
300 "CHIPNAME", chip);
301 co_return true;
302 }
303
Daniel Hsu61e12672025-06-12 19:20:46 +0800304 lg2::error("The device id doesn't match with chip.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800305 co_return false;
306}
307
308sdbusplus::async::task<bool> CpldLatticeManager::enableProgramMode()
309{
Daniel Hsu37a30142025-06-12 17:57:24 +0800310 std::vector<uint8_t> request = {commandEnableConfigMode, 0x08, 0x0, 0x0};
311 std::vector<uint8_t> response;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800312
Daniel Hsu37a30142025-06-12 17:57:24 +0800313 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800314 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800315 lg2::error("Failed to send enable program mode request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800316 co_return false;
317 }
318
319 if (!(co_await waitBusyAndVerify()))
320 {
321 lg2::error("Wait busy and verify fail");
322 co_return false;
323 }
324 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
325 co_return true;
326}
327
328sdbusplus::async::task<bool> CpldLatticeManager::eraseFlash()
329{
Daniel Hsu37a30142025-06-12 17:57:24 +0800330 std::vector<uint8_t> request;
331 std::vector<uint8_t> response;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800332
333 if (isLCMXO3D)
334 {
335 /*
336 Erase the different internal
337 memories. The bit in YYY defines
338 which memory is erased in Flash
339 access mode.
340 Bit 1=Enable
341 8 Erase CFG0
342 9 Erase CFG1
343 10 Erase UFM0
344 11 Erase UFM1
345 12 Erase UFM2
346 13 Erase UFM3
347 14 Erase CSEC
348 15 Erase USEC
349 16 Erase PUBKEY
350 17 Erase AESKEY
351 18 Erase FEA
352 19 Reserved
353 commandEraseFlash = 0x0E, 0Y YY 00
354 */
355 if (target.empty() || target == "CFG0")
356 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800357 request = {commandEraseFlash, 0x00, 0x01, 0x00};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800358 }
359 else if (target == "CFG1")
360 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800361 request = {commandEraseFlash, 0x00, 0x02, 0x00};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800362 }
363 else
364 {
365 lg2::error("Error: unknown target.");
366 co_return false;
367 }
368 }
369 else
370 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800371 request = {commandEraseFlash, 0xC, 0x0, 0x0};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800372 }
373
Daniel Hsu37a30142025-06-12 17:57:24 +0800374 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800375 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800376 lg2::error("Failed to send erase flash request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800377 co_return false;
378 }
379
380 if (!(co_await waitBusyAndVerify()))
381 {
382 lg2::error("Wait busy and verify fail");
383 co_return false;
384 }
385 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
386 co_return true;
387}
388
389sdbusplus::async::task<bool> CpldLatticeManager::resetConfigFlash()
390{
Daniel Hsu37a30142025-06-12 17:57:24 +0800391 std::vector<uint8_t> request;
392 std::vector<uint8_t> response;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800393 if (isLCMXO3D)
394 {
395 /*
396 Set Page Address pointer to the
397 beginning of the different internal
398 Flash sectors. The bit in YYYY
399 defines which sector is selected.
400 Bit Flash sector selected
401 8 CFG0
402 9 CFG1
403 10 FEA
404 11 PUBKEY
405 12 AESKEY
406 13 CSEC
407 14 UFM0
408 15 UFM1
409 16 UFM2
410 17 UFM3
411 18 USEC
412 19 Reserved
413 20 Reserved
414 21 Reserved
415 22 Reserved
416 commandResetConfigFlash = 0x46, YY YY 00
417 */
418 if (target.empty() || target == "CFG0")
419 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800420 request = {commandResetConfigFlash, 0x00, 0x01, 0x00};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800421 }
422 else if (target == "CFG1")
423 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800424 request = {commandResetConfigFlash, 0x00, 0x02, 0x00};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800425 }
426 else
427 {
428 lg2::error(
429 "Error: unknown target. Only CFG0 and CFG1 are supported.");
430 co_return false;
431 }
432 }
433 else
434 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800435 request = {commandResetConfigFlash, 0x0, 0x0, 0x0};
Daniel Hsuf6470b52025-02-26 15:03:47 +0800436 }
437
Daniel Hsu37a30142025-06-12 17:57:24 +0800438 co_return i2cInterface.sendReceive(request, response);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800439}
440
Potin Lai7b925762025-08-18 16:20:29 +0800441sdbusplus::async::task<bool> CpldLatticeManager::programSinglePage(
442 uint16_t pageOffset, std::span<const uint8_t> pageData)
443{
444 // Set Page Offset
445 std::vector<uint8_t> emptyResp(0);
446 std::vector<uint8_t> setPageAddrCmd = {
447 commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
448 setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
449 setPageAddrCmd[7] = static_cast<uint8_t>(pageOffset); // low byte
450
451 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch)
452 bool success = co_await i2cInterface.sendReceive(
453 setPageAddrCmd.data(), setPageAddrCmd.size(), nullptr, 0);
454 if (!success)
455 {
456 lg2::error("Write page address failed");
457 co_return false;
458 }
459
460 // Write Page Data
461 constexpr uint8_t pageCount = 1;
462 std::vector<uint8_t> writeCmd = {commandProgramPage, 0x0, 0x0, pageCount};
463 writeCmd.insert(writeCmd.end(), pageData.begin(), pageData.end());
464
465 success = co_await i2cInterface.sendReceive(writeCmd.data(),
466 writeCmd.size(), nullptr, 0);
467 if (!success)
468 {
469 lg2::error("Write page data failed");
470 co_return false;
471 }
472
473 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(200));
474
475 if (!(co_await waitBusyAndVerify()))
476 {
477 lg2::error("Wait busy and verify fail");
478 co_return false;
479 }
480
481 co_return true;
482}
483
484sdbusplus::async::task<bool> CpldLatticeManager::verifySinglePage(
485 uint16_t pageOffset, std::span<const uint8_t> pageData)
486{
487 // Set Page Offset
488 std::vector<uint8_t> emptyResp(0);
489 std::vector<uint8_t> setPageAddrCmd = {
490 commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00};
491 setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte
492 setPageAddrCmd[7] = static_cast<uint8_t>(pageOffset); // low byte
493
494 if (!i2cInterface.sendReceive(setPageAddrCmd, emptyResp))
495 {
496 lg2::error("Write page address failed");
497 co_return false;
498 }
499
500 // Read Page Data
501 constexpr uint8_t pageCount = 1;
502 std::vector<uint8_t> readData(pageData.size());
503 std::vector<uint8_t> readCmd = {commandReadPage, 0x0, 0x0, pageCount};
504
505 if (!i2cInterface.sendReceive(readCmd, readData))
506 {
507 lg2::error("Read page data failed");
508 co_return false;
509 }
510
511 constexpr size_t pageSize = 16;
512 auto mismatch_pair =
513 std::mismatch(pageData.begin(), pageData.end(), readData.begin());
514 if (mismatch_pair.first != pageData.end())
515 {
516 size_t idx = std::distance(pageData.begin(), mismatch_pair.first);
517 lg2::error("Verify failed at {INDEX}", "INDEX",
518 ((static_cast<size_t>(pageSize * pageOffset)) + idx));
519 co_return false;
520 }
521
522 co_return true;
523}
524
Daniel Hsuf6470b52025-02-26 15:03:47 +0800525sdbusplus::async::task<bool> CpldLatticeManager::writeProgramPage()
526{
527 /*
528 Program one NVCM/Flash page. Can be
529 used to program the NVCM0/CFG or
530 NVCM1/UFM.
531 */
Daniel Hsuf6470b52025-02-26 15:03:47 +0800532 size_t iterSize = 16;
533
Potin Lai7b925762025-08-18 16:20:29 +0800534 for (size_t i = 0; (i * iterSize) < fwInfo.cfgData.size(); i++)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800535 {
Potin Lai7b925762025-08-18 16:20:29 +0800536 size_t byteOffset = i * iterSize;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800537 double progressRate =
Potin Lai7b925762025-08-18 16:20:29 +0800538 ((double(byteOffset) / double(fwInfo.cfgData.size())) * 100);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800539 std::cout << "Update :" << std::fixed << std::dec
540 << std::setprecision(2) << progressRate << "% \r";
541
Potin Lai7b925762025-08-18 16:20:29 +0800542 uint8_t len = ((byteOffset + iterSize) < fwInfo.cfgData.size())
Daniel Hsuf6470b52025-02-26 15:03:47 +0800543 ? iterSize
Potin Lai7b925762025-08-18 16:20:29 +0800544 : (fwInfo.cfgData.size() - byteOffset);
545 auto pageData = std::vector<uint8_t>(
546 fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(byteOffset),
547 fwInfo.cfgData.begin() +
548 static_cast<std::ptrdiff_t>(byteOffset + len));
Daniel Hsuf6470b52025-02-26 15:03:47 +0800549
Potin Lai7b925762025-08-18 16:20:29 +0800550 size_t retry = 0;
551 const size_t maxWriteRetry = 10;
552 while (retry < maxWriteRetry)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800553 {
Potin Lai7b925762025-08-18 16:20:29 +0800554 if (!(co_await programSinglePage(i, pageData)))
555 {
556 retry++;
557 continue;
558 }
559
560 if (!(co_await verifySinglePage(i, pageData)))
561 {
562 retry++;
563 continue;
564 }
565
566 break;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800567 }
568
Potin Lai7b925762025-08-18 16:20:29 +0800569 if (retry >= maxWriteRetry)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800570 {
Potin Lai7b925762025-08-18 16:20:29 +0800571 lg2::error("Program and verify page failed");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800572 co_return false;
573 }
Potin Lai7b925762025-08-18 16:20:29 +0800574 }
Daniel Hsuf6470b52025-02-26 15:03:47 +0800575
Potin Lai7b925762025-08-18 16:20:29 +0800576 if (!(co_await waitBusyAndVerify()))
577 {
578 lg2::error("Wait busy and verify fail");
579 co_return false;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800580 }
581
582 co_return true;
583}
584
585sdbusplus::async::task<bool> CpldLatticeManager::programUserCode()
586{
Daniel Hsu37a30142025-06-12 17:57:24 +0800587 std::vector<uint8_t> request = {commandProgramUserCode, 0x0, 0x0, 0x0};
588 std::vector<uint8_t> response;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800589 for (int i = 3; i >= 0; i--)
590 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800591 request.push_back((fwInfo.version >> (i * 8)) & 0xFF);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800592 }
Daniel Hsuf6470b52025-02-26 15:03:47 +0800593
Daniel Hsu37a30142025-06-12 17:57:24 +0800594 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800595 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800596 lg2::error("Failed to send program user code request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800597 co_return false;
598 }
Daniel Hsuf6470b52025-02-26 15:03:47 +0800599 if (!(co_await waitBusyAndVerify()))
600 {
601 lg2::error("Wait busy and verify fail");
602 co_return false;
603 }
604
605 co_return true;
606}
607
608sdbusplus::async::task<bool> CpldLatticeManager::programDone()
609{
Daniel Hsu37a30142025-06-12 17:57:24 +0800610 std::vector<uint8_t> request = {commandProgramDone, 0x0, 0x0, 0x0};
611 std::vector<uint8_t> response;
Daniel Hsuf6470b52025-02-26 15:03:47 +0800612
Daniel Hsu37a30142025-06-12 17:57:24 +0800613 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800614 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800615 lg2::error("Failed to send program done request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800616 co_return false;
617 }
Daniel Hsu37a30142025-06-12 17:57:24 +0800618
Daniel Hsuf6470b52025-02-26 15:03:47 +0800619 if (!(co_await waitBusyAndVerify()))
620 {
621 lg2::error("Wait busy and verify fail");
622 co_return false;
623 }
624
625 co_return true;
626}
627
628sdbusplus::async::task<bool> CpldLatticeManager::disableConfigInterface()
629{
Daniel Hsu37a30142025-06-12 17:57:24 +0800630 std::vector<uint8_t> request = {commandDisableConfigInterface, 0x0, 0x0};
631 std::vector<uint8_t> response;
632 co_return i2cInterface.sendReceive(request, response);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800633}
634
635sdbusplus::async::task<bool> CpldLatticeManager::waitBusyAndVerify()
636{
637 uint8_t retry = 0;
638
639 while (retry <= busyWaitmaxRetry)
640 {
641 uint8_t busyFlag = 0xff;
642
Daniel Hsu37a30142025-06-12 17:57:24 +0800643 auto readBusyFlagResult = co_await readBusyFlag(busyFlag);
644 if (!readBusyFlagResult)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800645 {
646 lg2::error("Fail to read busy flag.");
647 co_return false;
648 }
649
650 if (busyFlag & busyFlagBit)
651 {
652 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
653 retry++;
654 if (retry > busyWaitmaxRetry)
655 {
656 lg2::error(
657 "Status Reg : Busy! Please check the I2C bus and address.");
658 co_return false;
659 }
660 }
661 else
662 {
663 break;
664 }
665 } // while loop busy check
666
667 // Check out status reg
Daniel Hsu37a30142025-06-12 17:57:24 +0800668 auto statusReg = std::make_unique<uint8_t>(0xff);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800669
Daniel Hsu37a30142025-06-12 17:57:24 +0800670 if (!(co_await readStatusReg(*statusReg)))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800671 {
672 lg2::error("Fail to read status register.");
673 co_return false;
674 }
675
Daniel Hsu37a30142025-06-12 17:57:24 +0800676 if (((*statusReg >> busyOrReadyBit) & 1) == isReady &&
677 ((*statusReg >> failOrOKBit) & 1) == isOK)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800678 {
679 lg2::debug("Status Reg : OK");
680 co_return true;
681 }
682
683 lg2::error("Status Reg : Fail! Please check the I2C bus and address.");
684 co_return false;
685}
686
687sdbusplus::async::task<bool> CpldLatticeManager::readBusyFlag(uint8_t& busyFlag)
688{
Daniel Hsuf6470b52025-02-26 15:03:47 +0800689 constexpr size_t resSize = 1;
Daniel Hsu37a30142025-06-12 17:57:24 +0800690 std::vector<uint8_t> request = {commandReadBusyFlag, 0x0, 0x0, 0x0};
691 std::vector<uint8_t> response(resSize, 0);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800692
Daniel Hsu37a30142025-06-12 17:57:24 +0800693 auto success = i2cInterface.sendReceive(request, response);
694 if (!success && response.size() != resSize)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800695 {
696 co_return false;
697 }
Daniel Hsu37a30142025-06-12 17:57:24 +0800698 busyFlag = response.at(0);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800699 co_return true;
700}
701
702sdbusplus::async::task<bool> CpldLatticeManager::readStatusReg(
703 uint8_t& statusReg)
704{
Daniel Hsu37a30142025-06-12 17:57:24 +0800705 std::vector<uint8_t> request = {commandReadStatusReg, 0x0, 0x0, 0x0};
706 std::vector<uint8_t> response(4, 0);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800707
Daniel Hsu37a30142025-06-12 17:57:24 +0800708 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800709 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800710 lg2::error("Failed to send read status register request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800711 co_return false;
712 }
713 /*
714 Read Status Register
715 [LSC_READ_STATUS]
716 0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
717 12 Busy Ready
718 13 Fail OK
Daniel Hsu37a30142025-06-12 17:57:24 +0800719 */
720 statusReg = response.at(2);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800721 co_return true;
722}
723
724sdbusplus::async::task<bool> CpldLatticeManager::readUserCode(
725 uint32_t& userCode)
726{
Daniel Hsuf6470b52025-02-26 15:03:47 +0800727 constexpr size_t resSize = 4;
Daniel Hsu37a30142025-06-12 17:57:24 +0800728 std::vector<uint8_t> request = {commandReadFwVersion, 0x0, 0x0, 0x0};
729 std::vector<uint8_t> response(resSize, 0);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800730
Daniel Hsu37a30142025-06-12 17:57:24 +0800731 if (!i2cInterface.sendReceive(request, response))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800732 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800733 lg2::error("Failed to send read user code request.");
Daniel Hsuf6470b52025-02-26 15:03:47 +0800734 co_return false;
735 }
736
737 for (size_t i = 0; i < resSize; i++)
738 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800739 userCode |= response.at(i) << ((3 - i) * 8);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800740 }
741 co_return true;
742}
743
744sdbusplus::async::task<bool> CpldLatticeManager::XO2XO3FamilyUpdate(
745 std::function<bool(int)> progressCallBack)
746{
747 if (progressCallBack == nullptr)
748 {
749 lg2::error("Error: progressCallBack is null.");
750 co_return false;
751 }
752
753 if (!(co_await readDeviceId()))
754 {
755 co_return false;
756 }
757 progressCallBack(10);
758
759 if (!jedFileParser())
760 {
761 lg2::error("JED file parsing failed");
762 co_return false;
763 }
764 progressCallBack(15);
765
766 if (!verifyChecksum())
767 {
768 lg2::error("Checksum verification failed");
769 co_return false;
770 }
771 progressCallBack(20);
772
773 if (!isLCMXO3D)
774 {
775 lg2::error("is not LCMXO3D");
776 }
777
778 lg2::debug("Starts to update ...");
779 lg2::debug("Enable program mode.");
780 progressCallBack(25);
781
782 co_await waitBusyAndVerify();
783
784 if (!(co_await enableProgramMode()))
785 {
786 lg2::error("Enable program mode failed.");
787 co_return false;
788 }
789 progressCallBack(30);
790
791 lg2::debug("Erase flash.");
792 if (!(co_await eraseFlash()))
793 {
794 lg2::error("Erase flash failed.");
795 co_return false;
796 }
797 progressCallBack(40);
798
799 lg2::debug("Reset config flash.");
800 if (!(co_await resetConfigFlash()))
801 {
802 lg2::error("Reset config flash failed.");
803 co_return false;
804 }
805 progressCallBack(50);
806
807 lg2::debug("Write program page ...");
808 if (!(co_await writeProgramPage()))
809 {
810 lg2::error("Write program page failed.");
811 co_return false;
812 }
813 lg2::debug("Write program page done.");
814 progressCallBack(60);
815
816 lg2::debug("Program user code.");
817 if (!(co_await programUserCode()))
818 {
819 lg2::error("Program user code failed.");
820 co_return false;
821 }
822 progressCallBack(70);
823
824 if (!(co_await programDone()))
825 {
826 lg2::error("Program not done.");
827 co_return false;
828 }
829 progressCallBack(80);
830
831 lg2::debug("Disable config interface.");
832 if (!(co_await disableConfigInterface()))
833 {
834 lg2::error("Disable Config Interface failed.");
835 co_return false;
836 }
837 progressCallBack(90);
838
839 lg2::debug("Update completed!");
840
841 co_return true;
842}
843
844sdbusplus::async::task<bool> CpldLatticeManager::updateFirmware(
845 std::function<bool(int)> progressCallBack)
846{
Daniel Hsu61e12672025-06-12 19:20:46 +0800847 co_return co_await XO2XO3FamilyUpdate(progressCallBack);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800848}
849
Daniel Hsuf6470b52025-02-26 15:03:47 +0800850sdbusplus::async::task<bool> CpldLatticeManager::getVersion(
851 std::string& version)
852{
Daniel Hsu37a30142025-06-12 17:57:24 +0800853 auto userCode = std::make_unique<uint32_t>(0);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800854
855 if (target.empty())
856 {
Daniel Hsu37a30142025-06-12 17:57:24 +0800857 if (!(co_await readUserCode(*userCode)))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800858 {
859 lg2::error("Read usercode failed.");
860 co_return false;
861 }
862
Daniel Hsu37a30142025-06-12 17:57:24 +0800863 lg2::debug("CPLD version: {VERSION}", "VERSION", *userCode);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800864 }
865 else if (target == "CFG0" || target == "CFG1")
866 {
867 isLCMXO3D = true;
868 co_await waitBusyAndVerify();
869
870 if (!(co_await enableProgramMode()))
871 {
872 lg2::error("Enable program mode failed.");
873 co_return false;
874 }
875
876 if (!(co_await resetConfigFlash()))
877 {
878 lg2::error("Reset config flash failed.");
879 co_return false;
880 }
881
Daniel Hsu37a30142025-06-12 17:57:24 +0800882 if (!(co_await readUserCode(*userCode)))
Daniel Hsuf6470b52025-02-26 15:03:47 +0800883 {
884 lg2::error("Read usercode failed.");
885 co_return false;
886 }
887
888 if (!(co_await programDone()))
889 {
890 lg2::error("Program not done.");
891 co_return false;
892 }
893
894 if (!(co_await disableConfigInterface()))
895 {
896 lg2::error("Disable Config Interface failed.");
897 co_return false;
898 }
899
900 lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
Daniel Hsu37a30142025-06-12 17:57:24 +0800901 "VERSION", *userCode);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800902 }
903 else
904 {
905 lg2::error("Error: unknown target.");
906 co_return false;
907 }
908
Daniel Hsu37a30142025-06-12 17:57:24 +0800909 if (*userCode == 0)
Daniel Hsuf6470b52025-02-26 15:03:47 +0800910 {
911 lg2::error("User code is zero, cannot get version.");
912 co_return false;
913 }
Daniel Hsu37a30142025-06-12 17:57:24 +0800914 version = uint32ToHexStr(*userCode);
Daniel Hsuf6470b52025-02-26 15:03:47 +0800915 co_return true;
916}