1 #include "lattice_xo3_cpld.hpp" 2 3 #include <phosphor-logging/lg2.hpp> 4 5 #include <fstream> 6 #include <vector> 7 8 namespace phosphor::software::cpld 9 { 10 11 sdbusplus::async::task<bool> LatticeXO3CPLD::readDeviceId() 12 { 13 std::vector<uint8_t> request = {commandReadDeviceId, 0x0, 0x0, 0x0}; 14 std::vector<uint8_t> response = {0, 0, 0, 0}; 15 16 if (!i2cInterface.sendReceive(request, response)) 17 { 18 lg2::error( 19 "Fail to read device Id. Please check the I2C bus and address."); 20 co_return false; 21 } 22 23 auto chipWantToUpdate = std::find_if( 24 supportedDeviceMap.begin(), supportedDeviceMap.end(), 25 [this](const auto& pair) { 26 auto chipModel = 27 getLatticeChipStr(pair.first, latticeStringType::modelString); 28 return chipModel == this->chip; 29 }); 30 31 if (chipWantToUpdate != supportedDeviceMap.end() && 32 chipWantToUpdate->second.deviceId == response) 33 { 34 if (chip.rfind("LCMXO3D", 0) == 0) 35 { 36 isLCMXO3D = true; 37 if (!target.empty() && target != "CFG0" && target != "CFG1") 38 { 39 lg2::error("Unknown target. Only CFG0 and CFG1 are supported."); 40 co_return false; 41 } 42 } 43 44 lg2::debug("Device ID match with chip. Chip name: {CHIPNAME}", 45 "CHIPNAME", chip); 46 co_return true; 47 } 48 49 lg2::error("The device id doesn't match with chip."); 50 co_return false; 51 } 52 53 sdbusplus::async::task<bool> LatticeXO3CPLD::eraseFlash() 54 { 55 std::vector<uint8_t> request; 56 std::vector<uint8_t> response; 57 58 if (isLCMXO3D) 59 { 60 /* 61 Erase the different internal 62 memories. The bit in YYY defines 63 which memory is erased in Flash 64 access mode. 65 Bit 1=Enable 66 8 Erase CFG0 67 9 Erase CFG1 68 10 Erase UFM0 69 11 Erase UFM1 70 12 Erase UFM2 71 13 Erase UFM3 72 14 Erase CSEC 73 15 Erase USEC 74 16 Erase PUBKEY 75 17 Erase AESKEY 76 18 Erase FEA 77 19 Reserved 78 commandEraseFlash = 0x0E, 0Y YY 00 79 */ 80 if (target.empty() || target == "CFG0") 81 { 82 request = {commandEraseFlash, 0x00, 0x01, 0x00}; 83 } 84 else if (target == "CFG1") 85 { 86 request = {commandEraseFlash, 0x00, 0x02, 0x00}; 87 } 88 else 89 { 90 lg2::error("Error: unknown target."); 91 co_return false; 92 } 93 } 94 else 95 { 96 request = {commandEraseFlash, 0xC, 0x0, 0x0}; 97 } 98 99 if (!i2cInterface.sendReceive(request, response)) 100 { 101 lg2::error("Failed to send erase flash request."); 102 co_return false; 103 } 104 105 if (!(co_await waitBusyAndVerify())) 106 { 107 lg2::error("Wait busy and verify fail"); 108 co_return false; 109 } 110 co_await sdbusplus::async::sleep_for(ctx, waitBusyTime); 111 co_return true; 112 } 113 114 sdbusplus::async::task<bool> LatticeXO3CPLD::writeProgramPage() 115 { 116 /* 117 Program one NVCM/Flash page. Can be 118 used to program the NVCM0/CFG or 119 NVCM1/UFM. 120 */ 121 size_t iterSize = 16; 122 123 for (size_t i = 0; (i * iterSize) < fwInfo.cfgData.size(); i++) 124 { 125 size_t byteOffset = i * iterSize; 126 double progressRate = 127 ((double(byteOffset) / double(fwInfo.cfgData.size())) * 100); 128 std::cout << "Update :" << std::fixed << std::dec 129 << std::setprecision(2) << progressRate << "% \r"; 130 131 uint8_t len = ((byteOffset + iterSize) < fwInfo.cfgData.size()) 132 ? iterSize 133 : (fwInfo.cfgData.size() - byteOffset); 134 auto pageData = std::vector<uint8_t>( 135 fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(byteOffset), 136 fwInfo.cfgData.begin() + 137 static_cast<std::ptrdiff_t>(byteOffset + len)); 138 139 size_t retry = 0; 140 const size_t maxWriteRetry = 10; 141 while (retry < maxWriteRetry) 142 { 143 if (!(co_await programSinglePage(i, pageData))) 144 { 145 retry++; 146 continue; 147 } 148 149 if (!(co_await verifySinglePage(i, pageData))) 150 { 151 retry++; 152 continue; 153 } 154 155 break; 156 } 157 158 if (retry >= maxWriteRetry) 159 { 160 lg2::error("Program and verify page failed"); 161 co_return false; 162 } 163 } 164 165 if (!(co_await waitBusyAndVerify())) 166 { 167 lg2::error("Wait busy and verify fail"); 168 co_return false; 169 } 170 171 co_return true; 172 } 173 174 sdbusplus::async::task<bool> LatticeXO3CPLD::readUserCode(uint32_t& userCode) 175 { 176 constexpr size_t resSize = 4; 177 std::vector<uint8_t> request = {commandReadFwVersion, 0x0, 0x0, 0x0}; 178 std::vector<uint8_t> response(resSize, 0); 179 180 if (!i2cInterface.sendReceive(request, response)) 181 { 182 lg2::error("Failed to send read user code request."); 183 co_return false; 184 } 185 186 for (size_t i = 0; i < resSize; i++) 187 { 188 userCode |= response.at(i) << ((3 - i) * 8); 189 } 190 co_return true; 191 } 192 193 sdbusplus::async::task<bool> LatticeXO3CPLD::programUserCode() 194 { 195 std::vector<uint8_t> request = {commandProgramUserCode, 0x0, 0x0, 0x0}; 196 std::vector<uint8_t> response; 197 for (int i = 3; i >= 0; i--) 198 { 199 request.push_back((fwInfo.version >> (i * 8)) & 0xFF); 200 } 201 202 if (!i2cInterface.sendReceive(request, response)) 203 { 204 lg2::error("Failed to send program user code request."); 205 co_return false; 206 } 207 if (!(co_await waitBusyAndVerify())) 208 { 209 lg2::error("Wait busy and verify fail"); 210 co_return false; 211 } 212 213 co_return true; 214 } 215 216 sdbusplus::async::task<bool> LatticeXO3CPLD::prepareUpdate(const uint8_t* image, 217 size_t imageSize) 218 { 219 if (!(co_await readDeviceId())) 220 { 221 co_return false; 222 } 223 224 if (!jedFileParser(image, imageSize)) 225 { 226 lg2::error("JED file parsing failed"); 227 co_return false; 228 } 229 lg2::debug("JED file parsing success"); 230 231 if (!verifyChecksum()) 232 { 233 lg2::error("Checksum verification failed"); 234 co_return false; 235 } 236 237 if (!isLCMXO3D) 238 { 239 lg2::error("is not LCMXO3D"); 240 } 241 242 co_return true; 243 } 244 245 sdbusplus::async::task<bool> LatticeXO3CPLD::doUpdate() 246 { 247 co_await waitBusyAndVerify(); 248 249 if (!(co_await enableProgramMode())) 250 { 251 lg2::error("Enable program mode failed."); 252 co_return false; 253 } 254 255 if (!(co_await eraseFlash())) 256 { 257 lg2::error("Erase flash failed."); 258 co_return false; 259 } 260 261 if (!(co_await resetConfigFlash())) 262 { 263 lg2::error("Reset config flash failed."); 264 co_return false; 265 } 266 267 if (!(co_await writeProgramPage())) 268 { 269 lg2::error("Write program page failed."); 270 co_return false; 271 } 272 273 if (!(co_await programUserCode())) 274 { 275 lg2::error("Program user code failed."); 276 co_return false; 277 } 278 279 if (!(co_await programDone())) 280 { 281 lg2::error("Program not done."); 282 co_return false; 283 } 284 285 co_return true; 286 } 287 288 sdbusplus::async::task<bool> LatticeXO3CPLD::finishUpdate() 289 { 290 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) 291 if (!(co_await disableConfigInterface())) 292 { 293 lg2::error("Disable Config Interface failed."); 294 co_return false; 295 } 296 297 co_return true; 298 } 299 300 sdbusplus::async::task<bool> LatticeXO3CPLD::programSinglePage( 301 uint16_t pageOffset, std::span<const uint8_t> pageData) 302 { 303 // Set Page Offset 304 std::vector<uint8_t> emptyResp(0); 305 std::vector<uint8_t> setPageAddrCmd = { 306 commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00}; 307 setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte 308 setPageAddrCmd[7] = static_cast<uint8_t>(pageOffset); // low byte 309 310 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) 311 bool success = co_await i2cInterface.sendReceive( 312 setPageAddrCmd.data(), setPageAddrCmd.size(), nullptr, 0); 313 if (!success) 314 { 315 lg2::error("Write page address failed"); 316 co_return false; 317 } 318 319 // Write Page Data 320 constexpr uint8_t pageCount = 1; 321 std::vector<uint8_t> writeCmd = {commandProgramPage, 0x0, 0x0, pageCount}; 322 writeCmd.insert(writeCmd.end(), pageData.begin(), pageData.end()); 323 324 success = co_await i2cInterface.sendReceive(writeCmd.data(), 325 writeCmd.size(), nullptr, 0); 326 if (!success) 327 { 328 lg2::error("Write page data failed"); 329 co_return false; 330 } 331 332 co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(200)); 333 334 if (!(co_await waitBusyAndVerify())) 335 { 336 lg2::error("Wait busy and verify fail"); 337 co_return false; 338 } 339 340 co_return true; 341 } 342 343 sdbusplus::async::task<bool> LatticeXO3CPLD::verifySinglePage( 344 uint16_t pageOffset, std::span<const uint8_t> pageData) 345 { 346 // Set Page Offset 347 std::vector<uint8_t> emptyResp(0); 348 std::vector<uint8_t> setPageAddrCmd = { 349 commandSetPageAddress, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00}; 350 setPageAddrCmd[6] = static_cast<uint8_t>(pageOffset >> 8); // high byte 351 setPageAddrCmd[7] = static_cast<uint8_t>(pageOffset); // low byte 352 353 if (!i2cInterface.sendReceive(setPageAddrCmd, emptyResp)) 354 { 355 lg2::error("Write page address failed"); 356 co_return false; 357 } 358 359 // Read Page Data 360 constexpr uint8_t pageCount = 1; 361 std::vector<uint8_t> readData(pageData.size()); 362 std::vector<uint8_t> readCmd = {commandReadPage, 0x0, 0x0, pageCount}; 363 364 if (!i2cInterface.sendReceive(readCmd, readData)) 365 { 366 lg2::error("Read page data failed"); 367 co_return false; 368 } 369 370 constexpr size_t pageSize = 16; 371 auto mismatch_pair = 372 std::mismatch(pageData.begin(), pageData.end(), readData.begin()); 373 if (mismatch_pair.first != pageData.end()) 374 { 375 size_t idx = std::distance(pageData.begin(), mismatch_pair.first); 376 lg2::error("Verify failed at {INDEX}", "INDEX", 377 ((static_cast<size_t>(pageSize * pageOffset)) + idx)); 378 co_return false; 379 } 380 381 co_return true; 382 } 383 384 } // namespace phosphor::software::cpld 385