xref: /openbmc/phosphor-bmc-code-mgmt/cpld/lattice/lattice_base_cpld.cpp (revision b602aad5026847e2a4895598c6b5b7b08a377282)
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