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
readDeviceId()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
eraseFlash()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
writeProgramPage()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
readUserCode(uint32_t & userCode)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
programUserCode()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
prepareUpdate(const uint8_t * image,size_t imageSize)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
doUpdate()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
finishUpdate()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
programSinglePage(uint16_t pageOffset,std::span<const uint8_t> pageData)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
verifySinglePage(uint16_t pageOffset,std::span<const uint8_t> pageData)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