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