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