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 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 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 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 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 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 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 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 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 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 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 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 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 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