1 #include "lattice_base_cpld.hpp"
2
3 #include <algorithm>
4 #include <cstddef>
5 #include <fstream>
6 #include <map>
7 #include <numeric>
8 #include <vector>
9
10 namespace phosphor::software::cpld
11 {
12
13 constexpr uint8_t busyWaitmaxRetry = 30;
14 constexpr uint8_t busyFlagBit = 0x80;
15
16 static constexpr std::string_view tagFuseQuantity = "QF";
17 static constexpr std::string_view tagUserCodeHex = "UH";
18 static constexpr std::string_view tagCFStart = "L000";
19 static constexpr std::string_view tagData = "NOTE TAG DATA";
20 static constexpr std::string_view tagUserFlashMemory = "NOTE USER MEMORY DATA";
21 static constexpr std::string_view tagChecksum = "C";
22 static constexpr std::string_view tagUserCode = "NOTE User Electronic";
23 static constexpr std::string_view tagEbrInitData = "NOTE EBR_INIT DATA";
24 static constexpr std::string_view tagEndConfig = "NOTE END CONFIG DATA";
25 static constexpr std::string_view tagDevName = "NOTE DEVICE NAME";
26
27 constexpr uint8_t isOK = 0;
28 constexpr uint8_t isReady = 0;
29 constexpr uint8_t busyOrReadyBit = 4;
30 constexpr uint8_t failOrOKBit = 5;
31
reverse_bit(uint8_t b)32 static uint8_t reverse_bit(uint8_t b)
33 {
34 b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
35 b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
36 b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
37 return b;
38 }
39
uint32ToHexStr(uint32_t value)40 std::string LatticeBaseCPLD::uint32ToHexStr(uint32_t value)
41 {
42 std::ostringstream oss;
43 oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
44 << value;
45 return oss.str();
46 }
47
updateFirmware(const uint8_t * image,size_t imageSize,std::function<bool (int)> progressCallBack)48 sdbusplus::async::task<bool> LatticeBaseCPLD::updateFirmware(
49 const uint8_t* image, size_t imageSize,
50 std::function<bool(int)> progressCallBack)
51 {
52 if (progressCallBack == nullptr)
53 {
54 lg2::error("Error: progressCallBack is null.");
55 co_return false;
56 }
57
58 if (!image || imageSize == 0)
59 {
60 lg2::error("Error: image is null.");
61 co_return false;
62 }
63
64 lg2::debug("CPLD image size: {IMAGESIZE}", "IMAGESIZE", imageSize);
65 auto result = co_await prepareUpdate(image, imageSize);
66 if (!result)
67 {
68 lg2::error("Prepare update failed.");
69 co_return false;
70 }
71 lg2::debug("Prepare update success");
72 progressCallBack(50);
73
74 result = co_await doUpdate();
75 if (!result)
76 {
77 lg2::error("Do update failed.");
78 co_return false;
79 }
80 lg2::debug("Do update success");
81 progressCallBack(90);
82
83 result = co_await finishUpdate();
84 if (!result)
85 {
86 lg2::error("Finish update failed.");
87 co_return false;
88 }
89 lg2::debug("Finish update success");
90 progressCallBack(100);
91
92 co_return true;
93 }
94
jedFileParser(const uint8_t * image,size_t imageSize)95 bool LatticeBaseCPLD::jedFileParser(const uint8_t* image, size_t imageSize)
96 {
97 enum class ParseState
98 {
99 none,
100 cfg,
101 endCfg,
102 ufm,
103 checksum,
104 userCode
105 };
106 ParseState state = ParseState::none;
107
108 if (image == nullptr || imageSize == 0)
109 {
110 lg2::error(
111 "Error: JED file is empty or not found. Please check the file.");
112 return false;
113 }
114
115 std::string content(reinterpret_cast<const char*>(image), imageSize);
116 std::istringstream iss(content);
117 std::string line;
118
119 auto pushPage = [](std::string& line, std::vector<uint8_t>& sector) {
120 if (line[0] == '0' || line[0] == '1')
121 {
122 while (line.size() >= 8)
123 {
124 try
125 {
126 sector.push_back(static_cast<uint8_t>(
127 std::stoi(line.substr(0, 8), 0, 2)));
128 line.erase(0, 8);
129 }
130 catch (...)
131 {
132 break;
133 }
134 }
135 }
136 };
137
138 while (getline(iss, line))
139 {
140 if (!line.empty() && line.back() == '\r')
141 {
142 line.pop_back();
143 }
144 if (line.empty())
145 {
146 continue;
147 }
148
149 if (line.starts_with(tagFuseQuantity))
150 {
151 ssize_t numberSize = static_cast<ssize_t>(line.find('*')) -
152 static_cast<ssize_t>(line.find('F')) - 1;
153 if (numberSize > 0)
154 {
155 fwInfo.fuseQuantity = std::stoul(
156 line.substr(tagFuseQuantity.length(), numberSize));
157 lg2::debug("fuseQuantity Size = {QFSIZE}", "QFSIZE",
158 fwInfo.fuseQuantity);
159 }
160 }
161 else if (line.starts_with(tagCFStart) ||
162 line.starts_with(tagEbrInitData))
163 {
164 state = ParseState::cfg;
165 continue;
166 }
167 else if (line.starts_with(tagEndConfig))
168 {
169 state = ParseState::endCfg;
170 continue;
171 }
172 else if (line.starts_with(tagUserFlashMemory) ||
173 line.starts_with(tagData))
174 {
175 state = ParseState::ufm;
176 continue;
177 }
178 else if (line.starts_with(tagUserCode))
179 {
180 state = ParseState::userCode;
181 continue;
182 }
183 else if (line.starts_with(tagChecksum))
184 {
185 state = ParseState::checksum;
186 }
187 else if (line.starts_with(tagDevName))
188 {
189 lg2::debug("{DEVNAME}", "DEVNAME", line);
190 if (line.find(chip) == std::string::npos)
191 {
192 lg2::debug("STOP UPDATING: The image does not match the chip.");
193 return -1;
194 }
195 }
196
197 switch (state)
198 {
199 case ParseState::cfg:
200 pushPage(line, fwInfo.cfgData);
201 break;
202 case ParseState::endCfg:
203 pushPage(line, sumOnly);
204 break;
205 case ParseState::ufm:
206 pushPage(line, fwInfo.ufmData);
207 break;
208 case ParseState::checksum:
209 if (line.size() > 1)
210 {
211 state = ParseState::none;
212 ssize_t numberSize =
213 static_cast<ssize_t>(line.find('*')) -
214 static_cast<ssize_t>(line.find('C')) - 1;
215 if (numberSize <= 0)
216 {
217 lg2::debug("Error in parsing checksum");
218 return -1;
219 }
220 static constexpr auto start = tagChecksum.length();
221 std::istringstream iss(line.substr(start, numberSize));
222 iss >> std::hex >> fwInfo.checksum;
223 lg2::debug("Checksum = 0x{CHECKSUM}", "CHECKSUM",
224 fwInfo.checksum);
225 }
226 break;
227 case ParseState::userCode:
228 if (line.starts_with(tagUserCodeHex))
229 {
230 state = ParseState::none;
231 ssize_t numberSize =
232 static_cast<ssize_t>(line.find('*')) -
233 static_cast<ssize_t>(line.find('H')) - 1;
234 if (numberSize <= 0)
235 {
236 lg2::debug("Error in parsing usercode");
237 return -1;
238 }
239 std::istringstream iss(
240 line.substr(tagUserCodeHex.length(), numberSize));
241 iss >> std::hex >> fwInfo.version;
242 lg2::debug("UserCode = 0x{USERCODE}", "USERCODE",
243 fwInfo.version);
244 }
245 break;
246 default:
247 break;
248 }
249 }
250
251 lg2::debug("CFG Size = {CFGSIZE}", "CFGSIZE", fwInfo.cfgData.size());
252 if (!fwInfo.ufmData.empty())
253 {
254 lg2::debug("userFlashMemory size = {UFMSIZE}", "UFMSIZE",
255 fwInfo.ufmData.size());
256 }
257
258 return true;
259 }
260
verifyChecksum()261 bool LatticeBaseCPLD::verifyChecksum()
262 {
263 uint32_t calculated = 0U;
264 auto addByte = [](uint32_t sum, uint8_t byte) {
265 return sum + reverse_bit(byte);
266 };
267
268 calculated = std::accumulate(fwInfo.cfgData.begin(), fwInfo.cfgData.end(),
269 calculated, addByte);
270 calculated =
271 std::accumulate(sumOnly.begin(), sumOnly.end(), calculated, addByte);
272 calculated = std::accumulate(fwInfo.ufmData.begin(), fwInfo.ufmData.end(),
273 calculated, addByte);
274
275 lg2::debug("Calculated checksum = {CALCULATED}", "CALCULATED", lg2::hex,
276 calculated);
277 lg2::debug("Checksum from JED file = {JEDFILECHECKSUM}", "JEDFILECHECKSUM",
278 lg2::hex, fwInfo.checksum);
279
280 if (fwInfo.checksum != (calculated & 0xFFFF))
281 {
282 lg2::error("JED file checksum compare fail, "
283 "Calculated checksum = {CALCULATED}, "
284 "Checksum from JED file = {JEDFILECHECKSUM}",
285 "CALCULATED", lg2::hex, calculated, "JEDFILECHECKSUM",
286 lg2::hex, fwInfo.checksum);
287 return false;
288 }
289
290 lg2::debug("JED file checksum compare success");
291 return true;
292 }
293
enableProgramMode()294 sdbusplus::async::task<bool> LatticeBaseCPLD::enableProgramMode()
295 {
296 std::vector<uint8_t> request = {commandEnableConfigMode, 0x08, 0x0, 0x0};
297 std::vector<uint8_t> response;
298
299 if (!i2cInterface.sendReceive(request, response))
300 {
301 lg2::error("Failed to send enable program mode request.");
302 co_return false;
303 }
304
305 if (!(co_await waitBusyAndVerify()))
306 {
307 lg2::error("Wait busy and verify fail");
308 co_return false;
309 }
310 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
311 co_return true;
312 }
313
resetConfigFlash()314 sdbusplus::async::task<bool> LatticeBaseCPLD::resetConfigFlash()
315 {
316 std::vector<uint8_t> request;
317 std::vector<uint8_t> response;
318 if (isLCMXO3D)
319 {
320 /*
321 Set Page Address pointer to the
322 beginning of the different internal
323 Flash sectors. The bit in YYYY
324 defines which sector is selected.
325 Bit Flash sector selected
326 8 CFG0
327 9 CFG1
328 10 FEA
329 11 PUBKEY
330 12 AESKEY
331 13 CSEC
332 14 UFM0
333 15 UFM1
334 16 UFM2
335 17 UFM3
336 18 USEC
337 19 Reserved
338 20 Reserved
339 21 Reserved
340 22 Reserved
341 commandResetConfigFlash = 0x46, YY YY 00
342 */
343 if (target.empty() || target == "CFG0")
344 {
345 request = {commandResetConfigFlash, 0x00, 0x01, 0x00};
346 }
347 else if (target == "CFG1")
348 {
349 request = {commandResetConfigFlash, 0x00, 0x02, 0x00};
350 }
351 else
352 {
353 lg2::error(
354 "Error: unknown target. Only CFG0 and CFG1 are supported.");
355 co_return false;
356 }
357 }
358 else
359 {
360 request = {commandResetConfigFlash, 0x0, 0x0, 0x0};
361 }
362
363 co_return i2cInterface.sendReceive(request, response);
364 }
365
programDone()366 sdbusplus::async::task<bool> LatticeBaseCPLD::programDone()
367 {
368 std::vector<uint8_t> request = {commandProgramDone, 0x0, 0x0, 0x0};
369 std::vector<uint8_t> response;
370
371 if (!i2cInterface.sendReceive(request, response))
372 {
373 lg2::error("Failed to send program done request.");
374 co_return false;
375 }
376
377 if (!(co_await waitBusyAndVerify()))
378 {
379 lg2::error("Wait busy and verify fail");
380 co_return false;
381 }
382
383 co_return true;
384 }
385
disableConfigInterface()386 sdbusplus::async::task<bool> LatticeBaseCPLD::disableConfigInterface()
387 {
388 std::vector<uint8_t> request = {commandDisableConfigInterface, 0x0, 0x0};
389 std::vector<uint8_t> response;
390 co_return i2cInterface.sendReceive(request, response);
391 }
392
waitBusyAndVerify()393 sdbusplus::async::task<bool> LatticeBaseCPLD::waitBusyAndVerify()
394 {
395 uint8_t retry = 0;
396
397 while (retry <= busyWaitmaxRetry)
398 {
399 uint8_t busyFlag = 0xff;
400
401 auto readBusyFlagResult = co_await readBusyFlag(busyFlag);
402 if (!readBusyFlagResult)
403 {
404 lg2::error("Fail to read busy flag.");
405 co_return false;
406 }
407
408 if (busyFlag & busyFlagBit)
409 {
410 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime);
411 retry++;
412 if (retry > busyWaitmaxRetry)
413 {
414 lg2::error(
415 "Status Reg : Busy! Please check the I2C bus and address.");
416 co_return false;
417 }
418 }
419 else
420 {
421 break;
422 }
423 } // while loop busy check
424
425 // Check out status reg
426 auto statusReg = std::make_unique<uint8_t>(0xff);
427
428 if (!(co_await readStatusReg(*statusReg)))
429 {
430 lg2::error("Fail to read status register.");
431 co_return false;
432 }
433
434 if (((*statusReg >> busyOrReadyBit) & 1) == isReady &&
435 ((*statusReg >> failOrOKBit) & 1) == isOK)
436 {
437 lg2::debug("Status Reg : OK");
438 co_return true;
439 }
440
441 lg2::error("Status Reg : Fail! Please check the I2C bus and address.");
442 co_return false;
443 }
444
readBusyFlag(uint8_t & busyFlag)445 sdbusplus::async::task<bool> LatticeBaseCPLD::readBusyFlag(uint8_t& busyFlag)
446 {
447 constexpr size_t resSize = 1;
448 std::vector<uint8_t> request = {commandReadBusyFlag, 0x0, 0x0, 0x0};
449 std::vector<uint8_t> response(resSize, 0);
450
451 auto success = i2cInterface.sendReceive(request, response);
452 if (!success && response.size() != resSize)
453 {
454 co_return false;
455 }
456 busyFlag = response.at(0);
457 co_return true;
458 }
459
readStatusReg(uint8_t & statusReg)460 sdbusplus::async::task<bool> LatticeBaseCPLD::readStatusReg(uint8_t& statusReg)
461 {
462 std::vector<uint8_t> request = {commandReadStatusReg, 0x0, 0x0, 0x0};
463 std::vector<uint8_t> response(4, 0);
464
465 if (!i2cInterface.sendReceive(request, response))
466 {
467 lg2::error("Failed to send read status register request.");
468 co_return false;
469 }
470 /*
471 Read Status Register
472 [LSC_READ_STATUS]
473 0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
474 12 Busy Ready
475 13 Fail OK
476 */
477 statusReg = response.at(2);
478 co_return true;
479 }
480
getVersion(std::string & version)481 sdbusplus::async::task<bool> LatticeBaseCPLD::getVersion(std::string& version)
482 {
483 auto userCode = std::make_unique<uint32_t>(0);
484
485 if (target.empty())
486 {
487 if (!(co_await readUserCode(*userCode)))
488 {
489 lg2::error("Read usercode failed.");
490 co_return false;
491 }
492
493 lg2::debug("CPLD version: {VERSION}", "VERSION", *userCode);
494 }
495 else if (target == "CFG0" || target == "CFG1")
496 {
497 isLCMXO3D = true;
498 co_await waitBusyAndVerify();
499
500 if (!(co_await enableProgramMode()))
501 {
502 lg2::error("Enable program mode failed.");
503 co_return false;
504 }
505
506 if (!(co_await resetConfigFlash()))
507 {
508 lg2::error("Reset config flash failed.");
509 co_return false;
510 }
511
512 if (!(co_await readUserCode(*userCode)))
513 {
514 lg2::error("Read usercode failed.");
515 co_return false;
516 }
517
518 if (!(co_await programDone()))
519 {
520 lg2::error("Program not done.");
521 co_return false;
522 }
523
524 if (!(co_await disableConfigInterface()))
525 {
526 lg2::error("Disable Config Interface failed.");
527 co_return false;
528 }
529
530 lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
531 "VERSION", *userCode);
532 }
533 else
534 {
535 lg2::error("Error: unknown target.");
536 co_return false;
537 }
538
539 if (*userCode == 0)
540 {
541 lg2::error("User code is zero, cannot get version.");
542 co_return false;
543 }
544 version = uint32ToHexStr(*userCode);
545 co_return true;
546 }
547
548 } // namespace phosphor::software::cpld
549