blob: ce70fa37d27fe27e4fdbdc7777d372c0ce0d47a2 [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>
6#include <fstream>
7#include <map>
8#include <thread>
9#include <vector>
10
11using sdbusplus::async::details::context_friend;
12
13constexpr uint8_t busyWaitmaxRetry = 30;
14constexpr uint8_t busyFlagBit = 0x80;
15constexpr std::chrono::milliseconds waitBusyTime(200);
16
17static constexpr std::string_view tagQF = "QF";
18static constexpr std::string_view tagUH = "UH";
19static constexpr std::string_view tagCFStart = "L000";
20static constexpr std::string_view tagChecksum = "C";
21static constexpr std::string_view tagUserCode = "NOTE User Electronic";
22static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
23
24constexpr uint8_t isOK = 0;
25constexpr uint8_t isReady = 0;
26constexpr uint8_t busyOrReadyBit = 4;
27constexpr uint8_t failOrOKBit = 5;
28
29constexpr bool enableUpdateEbrInit = false;
30
31enum cpldI2cCmd
32{
33 commandEraseFlash = 0x0E,
34 commandDisableConfigInterface = 0x26,
35 commandReadStatusReg = 0x3C,
36 commandResetConfigFlash = 0x46,
37 commandProgramDone = 0x5E,
38 commandProgramPage = 0x70,
39 commandEnableConfigMode = 0x74,
40 commandReadFwVersion = 0xC0,
41 commandProgramUserCode = 0xC2,
42 commandReadDeviceId = 0xE0,
43 commandReadBusyFlag = 0xF0,
44};
45
46static uint8_t reverse_bit(uint8_t b)
47{
48 b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
49 b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
50 b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
51 return b;
52}
53
54const std::map<std::string, std::vector<uint8_t>> xo2xo3DeviceIdMap = {
55 {"LCMXO3LF-4300C", {0x61, 0x2b, 0xc0, 0x43}},
56 {"LCMXO3LF-4300", {0x61, 0x2b, 0xc0, 0x43}},
57 {"LCMXO3LF-6900", {0x61, 0x2b, 0xd0, 0x43}},
58 {"LCMXO3D-4300", {0x01, 0x2e, 0x20, 0x43}},
59 {"LCMXO3D-9400", {0x21, 0x2e, 0x30, 0x43}},
60};
61
62static int findNumberSize(const std::string& end, const std::string& start,
63 const std::string& line)
64{
65 auto pos1 = line.find(start);
66 auto pos2 = line.find(end);
67
68 if (pos1 == std::string::npos || pos2 == std::string::npos || pos1 >= pos2)
69 {
70 return false;
71 }
72
73 return static_cast<int>(pos2 - pos1 - 1);
74}
75
76bool CpldLatticeManager::jedFileParser()
77{
78 bool cfStart = false;
79 bool ufmStart = false; // for isLCMXO3D
80 bool ufmPrepare = false;
81 bool versionStart = false;
82 bool checksumStart = false;
83 bool ebrInitDataStart = false;
84 int numberSize = 0;
85
86 if (image == nullptr || imageSize == 0)
87 {
88 lg2::error(
89 "Error: JED file is empty or not found. Please check the file.");
90 return false;
91 }
92
93 // Convert binary data to a string
94 std::string content(reinterpret_cast<const char*>(image), imageSize);
95 // Use stringstream to simulate file reading
96 std::istringstream iss(content);
97 std::string line;
98
99 // Parsing JED file
100 while (getline(iss, line))
101 {
102 if (line.rfind(tagQF, 0) == 0)
103 {
104 numberSize = findNumberSize("*", "F", line);
105 if (numberSize <= 0)
106 {
107 lg2::error("Error in parsing QF tag");
108 return false;
109 }
110 static constexpr auto start = tagQF.length();
111 fwInfo.QF = std::stoul(line.substr(start, numberSize));
112
113 lg2::debug("QF Size = {QF}", "QF", fwInfo.QF);
114 }
115 else if (line.rfind(tagCFStart, 0) == 0)
116 {
117 cfStart = true;
118 }
119 else if (enableUpdateEbrInit && line.rfind(tagEbrInitData, 0) == 0)
120 {
121 ebrInitDataStart = true;
122 }
123 else if (ufmPrepare)
124 {
125 ufmPrepare = false;
126 ufmStart = true;
127 continue;
128 }
129 else if (line.rfind(tagUserCode, 0) == 0)
130 {
131 versionStart = true;
132 }
133 else if (line.rfind(tagChecksum, 0) == 0)
134 {
135 checksumStart = true;
136 }
137
138 if (line.rfind("NOTE DEVICE NAME:", 0) == 0)
139 {
140 lg2::error(line.c_str());
141 if (line.find(chip) != std::string::npos)
142 {
143 lg2::debug("[OK] The image device name match with chip name");
144 }
145 else
146 {
147 lg2::debug("STOP UPDATEING: The image not match with chip.");
148 return false;
149 }
150 }
151
152 if (cfStart)
153 {
154 // L000
155 if ((line.rfind(tagCFStart, 0)) && (line.size() != 1))
156 {
157 if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
158 {
159 while (!line.empty())
160 {
161 auto binaryStr = line.substr(0, 8);
162 try
163 {
164 fwInfo.cfgData.push_back(
165 std::stoi(binaryStr, 0, 2));
166 line.erase(0, 8);
167 }
168 catch (const std::invalid_argument& error)
169 {
170 break;
171 }
172 catch (...)
173 {
174 lg2::error("Error while parsing CF section");
175 return false;
176 }
177 }
178 }
179 else
180 {
181 lg2::debug("CF size = {CF}", "CF", fwInfo.cfgData.size());
182 cfStart = false;
183 if (!ebrInitDataStart)
184 {
185 ufmPrepare = true;
186 }
187 }
188 }
189 }
190 else if (enableUpdateEbrInit && ebrInitDataStart)
191 {
192 // NOTE EBR_INIT DATA
193 if ((line.rfind(tagEbrInitData, 0)) && (line.size() != 1))
194 {
195 if ((line.rfind('L', 0)) && (line.size() != 1))
196 {
197 if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
198 {
199 while (!line.empty())
200 {
201 auto binaryStr = line.substr(0, 8);
202 try
203 {
204 fwInfo.cfgData.push_back(
205 std::stoi(binaryStr, 0, 2));
206 line.erase(0, 8);
207 }
208 catch (const std::invalid_argument& error)
209 {
210 break;
211 }
212 catch (...)
213 {
214 lg2::error("Error while parsing CF section");
215 return false;
216 }
217 }
218 }
219 else
220 {
221 lg2::debug("CF size with EBR_INIT Data = {CF}", "CF",
222 fwInfo.cfgData.size());
223 ebrInitDataStart = false;
224 ufmPrepare = true;
225 }
226 }
227 }
228 }
229 else if ((checksumStart) && (line.size() != 1))
230 {
231 checksumStart = false;
232 numberSize = findNumberSize("*", "C", line);
233 if (numberSize <= 0)
234 {
235 lg2::error("Error in parsing checksum");
236 return false;
237 }
238 static constexpr auto start = tagChecksum.length();
239 std::istringstream iss(line.substr(start, numberSize));
240 iss >> std::hex >> fwInfo.checksum;
241
242 lg2::debug("Checksum = {CHECKSUM}", "CHECKSUM", fwInfo.checksum);
243 }
244 else if (versionStart)
245 {
246 if ((line.rfind(tagUserCode, 0)) && (line.size() != 1))
247 {
248 versionStart = false;
249
250 if (line.rfind(tagUH, 0) == 0)
251 {
252 numberSize = findNumberSize("*", "H", line);
253 if (numberSize <= 0)
254 {
255 lg2::error("Error in parsing version");
256 return false;
257 }
258 static constexpr auto start = tagUH.length();
259 std::istringstream iss(line.substr(start, numberSize));
260 iss >> std::hex >> fwInfo.version;
261
262 lg2::debug("UserCode = {USERCODE}", "USERCODE",
263 fwInfo.version);
264 }
265 }
266 }
267 else if (ufmStart)
268 {
269 if ((line.rfind('L', 0)) && (line.size() != 1))
270 {
271 if ((line.rfind('0', 0) == 0) || (line.rfind('1', 0) == 0))
272 {
273 while (!line.empty())
274 {
275 auto binaryStr = line.substr(0, 8);
276 try
277 {
278 fwInfo.ufmData.push_back(
279 std::stoi(binaryStr, 0, 2));
280 line.erase(0, 8);
281 }
282 catch (const std::invalid_argument& error)
283 {
284 break;
285 }
286 catch (...)
287 {
288 lg2::error("Error while parsing UFM section");
289 return false;
290 }
291 }
292 }
293 else
294 {
295 lg2::debug("UFM size = {UFM}", "UFM",
296 fwInfo.ufmData.size());
297 ufmStart = false;
298 }
299 }
300 }
301 }
302
303 return true;
304}
305
306bool CpldLatticeManager::verifyChecksum()
307{
308 // Compute check sum
309 unsigned int jedFileCheckSum = 0;
310 for (unsigned i = 0; i < fwInfo.cfgData.size(); i++)
311 {
312 jedFileCheckSum += reverse_bit(fwInfo.cfgData.at(i));
313 }
314 for (unsigned i = 0; i < fwInfo.ufmData.size(); i++)
315 {
316 jedFileCheckSum += reverse_bit(fwInfo.ufmData.at(i));
317 }
318 lg2::debug("jedFileCheckSum = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
319 jedFileCheckSum);
320 jedFileCheckSum = jedFileCheckSum & 0xffff;
321
322 if ((fwInfo.checksum != jedFileCheckSum) || (fwInfo.checksum == 0))
323 {
324 lg2::error("CPLD JED File CheckSum Error = {JEDFILECHECKSUM}",
325 "JEDFILECHECKSUM", jedFileCheckSum);
326 return false;
327 }
328
329 lg2::debug("JED File Checksum compare success");
330 return true;
331}
332
333sdbusplus::async::task<bool> CpldLatticeManager::readDeviceId()
334{
335 auto sched = context_friend::get_scheduler(ctx);
336 std::vector<uint8_t> command = {commandReadDeviceId, 0x0, 0x0, 0x0};
337 constexpr size_t resSize = 4;
338 std::vector<uint8_t> readData(resSize, 0);
339 bool success = co_await stdexec::starts_on(
340 sched, i2cInterface.sendReceive(command.data(), command.size(),
341 readData.data(), resSize));
342 if (!success)
343 {
344 lg2::error(
345 "Fail to read device Id. Please check the I2C bus and address.");
346 co_return false;
347 }
348
349 auto chipWantToUpdate = xo2xo3DeviceIdMap.find(chip);
350
351 if (chipWantToUpdate != xo2xo3DeviceIdMap.end() &&
352 chipWantToUpdate->second == readData)
353 {
354 if (chip.rfind("LCMXO3D", 0) == 0)
355 {
356 isLCMXO3D = true;
357 if (!target.empty() && target != "CFG0" && target != "CFG1")
358 {
359 lg2::error("Unknown target. Only CFG0 and CFG1 are supported.");
360 co_return false;
361 }
362 }
363
364 lg2::debug("Device ID match with chip. Chip name: {CHIPNAME}",
365 "CHIPNAME", chip);
366 co_return true;
367 }
368
369 lg2::error(
370 "The device id not match with chip. Only the following chip names are supported: ");
371 for (const auto& chip : xo2xo3DeviceIdMap)
372 {
373 lg2::error(chip.first.c_str());
374 }
375 co_return false;
376}
377
378sdbusplus::async::task<bool> CpldLatticeManager::enableProgramMode()
379{
380 auto sched = context_friend::get_scheduler(ctx);
381 std::vector<uint8_t> command = {commandEnableConfigMode, 0x08, 0x0, 0x0};
382 bool success = co_await stdexec::starts_on(
383 sched,
384 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
385
386 if (!success)
387 {
388 co_return false;
389 }
390
391 if (!(co_await waitBusyAndVerify()))
392 {
393 lg2::error("Wait busy and verify fail");
394 co_return false;
395 }
396 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
397 co_return true;
398}
399
400sdbusplus::async::task<bool> CpldLatticeManager::eraseFlash()
401{
402 auto sched = context_friend::get_scheduler(ctx);
403 std::vector<uint8_t> command;
404
405 if (isLCMXO3D)
406 {
407 /*
408 Erase the different internal
409 memories. The bit in YYY defines
410 which memory is erased in Flash
411 access mode.
412 Bit 1=Enable
413 8 Erase CFG0
414 9 Erase CFG1
415 10 Erase UFM0
416 11 Erase UFM1
417 12 Erase UFM2
418 13 Erase UFM3
419 14 Erase CSEC
420 15 Erase USEC
421 16 Erase PUBKEY
422 17 Erase AESKEY
423 18 Erase FEA
424 19 Reserved
425 commandEraseFlash = 0x0E, 0Y YY 00
426 */
427 if (target.empty() || target == "CFG0")
428 {
429 command = {commandEraseFlash, 0x00, 0x01, 0x00};
430 }
431 else if (target == "CFG1")
432 {
433 command = {commandEraseFlash, 0x00, 0x02, 0x00};
434 }
435 else
436 {
437 lg2::error("Error: unknown target.");
438 co_return false;
439 }
440 }
441 else
442 {
443 command = {commandEraseFlash, 0xC, 0x0, 0x0};
444 }
445
446 bool success = co_await stdexec::starts_on(
447 sched,
448 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
449 if (!success)
450 {
451 co_return false;
452 }
453
454 if (!(co_await waitBusyAndVerify()))
455 {
456 lg2::error("Wait busy and verify fail");
457 co_return false;
458 }
459 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
460 co_return true;
461}
462
463sdbusplus::async::task<bool> CpldLatticeManager::resetConfigFlash()
464{
465 auto sched = context_friend::get_scheduler(ctx);
466 std::vector<uint8_t> command;
467 if (isLCMXO3D)
468 {
469 /*
470 Set Page Address pointer to the
471 beginning of the different internal
472 Flash sectors. The bit in YYYY
473 defines which sector is selected.
474 Bit Flash sector selected
475 8 CFG0
476 9 CFG1
477 10 FEA
478 11 PUBKEY
479 12 AESKEY
480 13 CSEC
481 14 UFM0
482 15 UFM1
483 16 UFM2
484 17 UFM3
485 18 USEC
486 19 Reserved
487 20 Reserved
488 21 Reserved
489 22 Reserved
490 commandResetConfigFlash = 0x46, YY YY 00
491 */
492 if (target.empty() || target == "CFG0")
493 {
494 command = {commandResetConfigFlash, 0x00, 0x01, 0x00};
495 }
496 else if (target == "CFG1")
497 {
498 command = {commandResetConfigFlash, 0x00, 0x02, 0x00};
499 }
500 else
501 {
502 lg2::error(
503 "Error: unknown target. Only CFG0 and CFG1 are supported.");
504 co_return false;
505 }
506 }
507 else
508 {
509 command = {commandResetConfigFlash, 0x0, 0x0, 0x0};
510 }
511
512 co_return co_await stdexec::starts_on(
513 sched,
514 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
515}
516
517sdbusplus::async::task<bool> CpldLatticeManager::writeProgramPage()
518{
519 /*
520 Program one NVCM/Flash page. Can be
521 used to program the NVCM0/CFG or
522 NVCM1/UFM.
523 */
524 auto sched = context_friend::get_scheduler(ctx);
525 std::vector<uint8_t> command = {commandProgramPage, 0x0, 0x0, 0x01};
526 size_t iterSize = 16;
527
528 for (size_t i = 0; i < fwInfo.cfgData.size(); i += iterSize)
529 {
530 double progressRate =
531 ((double(i) / double(fwInfo.cfgData.size())) * 100);
532 std::cout << "Update :" << std::fixed << std::dec
533 << std::setprecision(2) << progressRate << "% \r";
534
535 uint8_t len = ((i + iterSize) < fwInfo.cfgData.size())
536 ? iterSize
537 : (fwInfo.cfgData.size() - i);
538 std::vector<uint8_t> data = command;
539
540 data.insert(
541 data.end(), fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(i),
542 fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(i + len));
543
544 bool success = co_await stdexec::starts_on(
545 sched,
546 i2cInterface.sendReceive(data.data(), data.size(), nullptr, 0));
547 if (!success)
548 {
549 co_return false;
550 }
551
552 /*
553 Reference spec
554 Important! If don't sleep, it will take a long time to update.
555 */
556 co_await sdbusplus::async::sleep_for(ctx,
557 std::chrono::microseconds(200));
558
559 if (!(co_await waitBusyAndVerify()))
560 {
561 lg2::error("Wait busy and verify fail");
562 co_return false;
563 }
564
565 data.clear();
566 }
567
568 co_return true;
569}
570
571sdbusplus::async::task<bool> CpldLatticeManager::programUserCode()
572{
573 auto sched = context_friend::get_scheduler(ctx);
574 std::vector<uint8_t> command = {commandProgramUserCode, 0x0, 0x0, 0x0};
575 for (int i = 3; i >= 0; i--)
576 {
577 command.push_back((fwInfo.version >> (i * 8)) & 0xFF);
578 }
579 bool success = co_await stdexec::starts_on(
580 sched,
581 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
582
583 if (!success)
584 {
585 co_return false;
586 }
587
588 if (!(co_await waitBusyAndVerify()))
589 {
590 lg2::error("Wait busy and verify fail");
591 co_return false;
592 }
593
594 co_return true;
595}
596
597sdbusplus::async::task<bool> CpldLatticeManager::programDone()
598{
599 auto sched = context_friend::get_scheduler(ctx);
600 std::vector<uint8_t> command = {commandProgramDone, 0x0, 0x0, 0x0};
601 bool success = co_await stdexec::starts_on(
602 sched,
603 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
604
605 if (!success)
606 {
607 co_return false;
608 }
609 if (!(co_await waitBusyAndVerify()))
610 {
611 lg2::error("Wait busy and verify fail");
612 co_return false;
613 }
614
615 co_return true;
616}
617
618sdbusplus::async::task<bool> CpldLatticeManager::disableConfigInterface()
619{
620 auto sched = context_friend::get_scheduler(ctx);
621 std::vector<uint8_t> command = {commandDisableConfigInterface, 0x0, 0x0};
622
623 bool success = co_await stdexec::starts_on(
624 sched,
625 i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
626
627 co_return success;
628}
629
630sdbusplus::async::task<bool> CpldLatticeManager::waitBusyAndVerify()
631{
632 uint8_t retry = 0;
633
634 while (retry <= busyWaitmaxRetry)
635 {
636 uint8_t busyFlag = 0xff;
637
638 if (!(co_await readBusyFlag(busyFlag)))
639 {
640 lg2::error("Fail to read busy flag.");
641 co_return false;
642 }
643
644 if (busyFlag & busyFlagBit)
645 {
646 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
647 retry++;
648 if (retry > busyWaitmaxRetry)
649 {
650 lg2::error(
651 "Status Reg : Busy! Please check the I2C bus and address.");
652 co_return false;
653 }
654 }
655 else
656 {
657 break;
658 }
659 } // while loop busy check
660
661 // Check out status reg
662 uint8_t statusReg = 0xff;
663
664 if (!(co_await readStatusReg(statusReg)))
665 {
666 lg2::error("Fail to read status register.");
667 co_return false;
668 }
669
670 if (((statusReg >> busyOrReadyBit) & 1) == isReady &&
671 ((statusReg >> failOrOKBit) & 1) == isOK)
672 {
673 lg2::debug("Status Reg : OK");
674 co_return true;
675 }
676
677 lg2::error("Status Reg : Fail! Please check the I2C bus and address.");
678 co_return false;
679}
680
681sdbusplus::async::task<bool> CpldLatticeManager::readBusyFlag(uint8_t& busyFlag)
682{
683 auto sched = context_friend::get_scheduler(ctx);
684 std::vector<uint8_t> command = {commandReadBusyFlag, 0x0, 0x0, 0x0};
685 constexpr size_t resSize = 1;
686 std::vector<uint8_t> readData(resSize, 0);
687 bool success = co_await stdexec::starts_on(
688 sched, i2cInterface.sendReceive(command.data(), command.size(),
689 readData.data(), resSize));
690
691 if (!success || (readData.size() != resSize))
692 {
693 co_return false;
694 }
695 busyFlag = readData.at(0);
696 co_return true;
697}
698
699sdbusplus::async::task<bool> CpldLatticeManager::readStatusReg(
700 uint8_t& statusReg)
701{
702 auto sched = context_friend::get_scheduler(ctx);
703 std::vector<uint8_t> command = {commandReadStatusReg, 0x0, 0x0, 0x0};
704 constexpr size_t resSize = 4;
705 std::vector<uint8_t> readData(resSize, 0);
706 bool success = co_await stdexec::starts_on(
707 sched, i2cInterface.sendReceive(command.data(), command.size(),
708 readData.data(), resSize));
709
710 if (!success || (readData.size() != resSize))
711 {
712 co_return false;
713 }
714 /*
715 Read Status Register
716 [LSC_READ_STATUS]
717 0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
718 12 Busy Ready
719 13 Fail OK
720 */
721 statusReg = readData.at(2);
722 co_return true;
723}
724
725sdbusplus::async::task<bool> CpldLatticeManager::readUserCode(
726 uint32_t& userCode)
727{
728 auto sched = context_friend::get_scheduler(ctx);
729 std::vector<uint8_t> command = {commandReadFwVersion, 0x0, 0x0, 0x0};
730 constexpr size_t resSize = 4;
731 std::vector<uint8_t> readData(resSize, 0);
732 bool success = co_await stdexec::starts_on(
733 sched, i2cInterface.sendReceive(command.data(), command.size(),
734 readData.data(), resSize));
735
736 if (!success)
737 {
738 co_return false;
739 }
740
741 for (size_t i = 0; i < resSize; i++)
742 {
743 userCode |= readData.at(i) << ((3 - i) * 8);
744 }
745 co_return true;
746}
747
748sdbusplus::async::task<bool> CpldLatticeManager::XO2XO3FamilyUpdate(
749 std::function<bool(int)> progressCallBack)
750{
751 if (progressCallBack == nullptr)
752 {
753 lg2::error("Error: progressCallBack is null.");
754 co_return false;
755 }
756
757 if (!(co_await readDeviceId()))
758 {
759 co_return false;
760 }
761 progressCallBack(10);
762
763 if (!jedFileParser())
764 {
765 lg2::error("JED file parsing failed");
766 co_return false;
767 }
768 progressCallBack(15);
769
770 if (!verifyChecksum())
771 {
772 lg2::error("Checksum verification failed");
773 co_return false;
774 }
775 progressCallBack(20);
776
777 if (!isLCMXO3D)
778 {
779 lg2::error("is not LCMXO3D");
780 }
781
782 lg2::debug("Starts to update ...");
783 lg2::debug("Enable program mode.");
784 progressCallBack(25);
785
786 co_await waitBusyAndVerify();
787
788 if (!(co_await enableProgramMode()))
789 {
790 lg2::error("Enable program mode failed.");
791 co_return false;
792 }
793 progressCallBack(30);
794
795 lg2::debug("Erase flash.");
796 if (!(co_await eraseFlash()))
797 {
798 lg2::error("Erase flash failed.");
799 co_return false;
800 }
801 progressCallBack(40);
802
803 lg2::debug("Reset config flash.");
804 if (!(co_await resetConfigFlash()))
805 {
806 lg2::error("Reset config flash failed.");
807 co_return false;
808 }
809 progressCallBack(50);
810
811 lg2::debug("Write program page ...");
812 if (!(co_await writeProgramPage()))
813 {
814 lg2::error("Write program page failed.");
815 co_return false;
816 }
817 lg2::debug("Write program page done.");
818 progressCallBack(60);
819
820 lg2::debug("Program user code.");
821 if (!(co_await programUserCode()))
822 {
823 lg2::error("Program user code failed.");
824 co_return false;
825 }
826 progressCallBack(70);
827
828 if (!(co_await programDone()))
829 {
830 lg2::error("Program not done.");
831 co_return false;
832 }
833 progressCallBack(80);
834
835 lg2::debug("Disable config interface.");
836 if (!(co_await disableConfigInterface()))
837 {
838 lg2::error("Disable Config Interface failed.");
839 co_return false;
840 }
841 progressCallBack(90);
842
843 lg2::debug("Update completed!");
844
845 co_return true;
846}
847
848sdbusplus::async::task<bool> CpldLatticeManager::updateFirmware(
849 std::function<bool(int)> progressCallBack)
850{
851 if (xo2xo3DeviceIdMap.find(chip) != xo2xo3DeviceIdMap.end())
852 {
853 co_return co_await XO2XO3FamilyUpdate(progressCallBack);
854 }
855 lg2::error("Unsupported chip type: {CHIP}", "CHIP", chip);
856 co_return false;
857}
858
859std::string uint32ToHexStr(uint32_t value)
860{
861 std::ostringstream oss;
862 oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
863 << value;
864 return oss.str();
865}
866
867sdbusplus::async::task<bool> CpldLatticeManager::getVersion(
868 std::string& version)
869{
870 uint32_t userCode = 0;
871
872 if (target.empty())
873 {
874 if (!(co_await readUserCode(userCode)))
875 {
876 lg2::error("Read usercode failed.");
877 co_return false;
878 }
879
880 lg2::debug("CPLD version: {VERSION}", "VERSION", userCode);
881 }
882 else if (target == "CFG0" || target == "CFG1")
883 {
884 isLCMXO3D = true;
885 co_await waitBusyAndVerify();
886
887 if (!(co_await enableProgramMode()))
888 {
889 lg2::error("Enable program mode failed.");
890 co_return false;
891 }
892
893 if (!(co_await resetConfigFlash()))
894 {
895 lg2::error("Reset config flash failed.");
896 co_return false;
897 }
898
899 if (!(co_await readUserCode(userCode)))
900 {
901 lg2::error("Read usercode failed.");
902 co_return false;
903 }
904
905 if (!(co_await programDone()))
906 {
907 lg2::error("Program not done.");
908 co_return false;
909 }
910
911 if (!(co_await disableConfigInterface()))
912 {
913 lg2::error("Disable Config Interface failed.");
914 co_return false;
915 }
916
917 lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
918 "VERSION", userCode);
919 }
920 else
921 {
922 lg2::error("Error: unknown target.");
923 co_return false;
924 }
925
926 if (userCode == 0)
927 {
928 lg2::error("User code is zero, cannot get version.");
929 co_return false;
930 }
931 version = uint32ToHexStr(userCode);
932 co_return true;
933}