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