xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mp297x.cpp (revision 3f2f3e6a61777c16094c94c22734659a8d84bdad)
1*3f2f3e6aSKevin Tung #include "mp297x.hpp"
2*3f2f3e6aSKevin Tung 
3*3f2f3e6aSKevin Tung #include "common/include/utils.hpp"
4*3f2f3e6aSKevin Tung 
5*3f2f3e6aSKevin Tung #include <phosphor-logging/lg2.hpp>
6*3f2f3e6aSKevin Tung 
7*3f2f3e6aSKevin Tung #include <fstream>
8*3f2f3e6aSKevin Tung 
9*3f2f3e6aSKevin Tung PHOSPHOR_LOG2_USING;
10*3f2f3e6aSKevin Tung 
11*3f2f3e6aSKevin Tung namespace phosphor::software::VR
12*3f2f3e6aSKevin Tung {
13*3f2f3e6aSKevin Tung 
14*3f2f3e6aSKevin Tung static constexpr size_t statusByteLength = 1;
15*3f2f3e6aSKevin Tung 
16*3f2f3e6aSKevin Tung static constexpr std::string_view crcUserRegName = "CRC_USER";
17*3f2f3e6aSKevin Tung static constexpr std::string_view crcMultiRegName = "CRC_MULTI";
18*3f2f3e6aSKevin Tung 
19*3f2f3e6aSKevin Tung enum class MP297XCmd : uint8_t
20*3f2f3e6aSKevin Tung {
21*3f2f3e6aSKevin Tung     // Page 0 commands
22*3f2f3e6aSKevin Tung     storeDataIntoMTP = 0xF1,
23*3f2f3e6aSKevin Tung 
24*3f2f3e6aSKevin Tung     // Page 1 commands
25*3f2f3e6aSKevin Tung     writeProtectMode = 0x35,
26*3f2f3e6aSKevin Tung     enableMTPPageWR = 0x4F,
27*3f2f3e6aSKevin Tung 
28*3f2f3e6aSKevin Tung     // Page 2 commands
29*3f2f3e6aSKevin Tung     enableMultiConfigCRC = 0xF3,
30*3f2f3e6aSKevin Tung 
31*3f2f3e6aSKevin Tung     // Page 0x29 commands
32*3f2f3e6aSKevin Tung     readUserCodeCRC = 0xFF,
33*3f2f3e6aSKevin Tung 
34*3f2f3e6aSKevin Tung     // Page 0x2A commands
35*3f2f3e6aSKevin Tung     readMultiConfigCRC = 0xBF,
36*3f2f3e6aSKevin Tung };
37*3f2f3e6aSKevin Tung 
parseDeviceConfiguration()38*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::parseDeviceConfiguration()
39*3f2f3e6aSKevin Tung {
40*3f2f3e6aSKevin Tung     if (!configuration)
41*3f2f3e6aSKevin Tung     {
42*3f2f3e6aSKevin Tung         error("Device configuration not initialized");
43*3f2f3e6aSKevin Tung         co_return false;
44*3f2f3e6aSKevin Tung     }
45*3f2f3e6aSKevin Tung 
46*3f2f3e6aSKevin Tung     configuration->vendorId = 0x0025;
47*3f2f3e6aSKevin Tung     configuration->productId = 0x0071;
48*3f2f3e6aSKevin Tung 
49*3f2f3e6aSKevin Tung     for (const auto& tokens : parser->lineTokens)
50*3f2f3e6aSKevin Tung     {
51*3f2f3e6aSKevin Tung         if (!parser->isValidDataTokens(tokens))
52*3f2f3e6aSKevin Tung         {
53*3f2f3e6aSKevin Tung             continue;
54*3f2f3e6aSKevin Tung         }
55*3f2f3e6aSKevin Tung 
56*3f2f3e6aSKevin Tung         auto regName = parser->getVal<std::string>(tokens, ATE::regName);
57*3f2f3e6aSKevin Tung 
58*3f2f3e6aSKevin Tung         if (regName == crcUserRegName)
59*3f2f3e6aSKevin Tung         {
60*3f2f3e6aSKevin Tung             configuration->configId =
61*3f2f3e6aSKevin Tung                 parser->getVal<uint32_t>(tokens, ATE::configId);
62*3f2f3e6aSKevin Tung             configuration->crcUser =
63*3f2f3e6aSKevin Tung                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
64*3f2f3e6aSKevin Tung         }
65*3f2f3e6aSKevin Tung         else if (regName == crcMultiRegName)
66*3f2f3e6aSKevin Tung         {
67*3f2f3e6aSKevin Tung             configuration->crcMulti =
68*3f2f3e6aSKevin Tung                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
69*3f2f3e6aSKevin Tung             break;
70*3f2f3e6aSKevin Tung         }
71*3f2f3e6aSKevin Tung     }
72*3f2f3e6aSKevin Tung 
73*3f2f3e6aSKevin Tung     co_return true;
74*3f2f3e6aSKevin Tung }
75*3f2f3e6aSKevin Tung 
verifyImage(const uint8_t * image,size_t imageSize)76*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::verifyImage(const uint8_t* image,
77*3f2f3e6aSKevin Tung                                                  size_t imageSize)
78*3f2f3e6aSKevin Tung {
79*3f2f3e6aSKevin Tung     if (!co_await parseImage(image, imageSize))
80*3f2f3e6aSKevin Tung     {
81*3f2f3e6aSKevin Tung         error("Image verification failed: image parsing failed");
82*3f2f3e6aSKevin Tung         co_return false;
83*3f2f3e6aSKevin Tung     }
84*3f2f3e6aSKevin Tung 
85*3f2f3e6aSKevin Tung     if (configuration->registersData.empty())
86*3f2f3e6aSKevin Tung     {
87*3f2f3e6aSKevin Tung         error("Image verification failed - no data found");
88*3f2f3e6aSKevin Tung         co_return false;
89*3f2f3e6aSKevin Tung     }
90*3f2f3e6aSKevin Tung 
91*3f2f3e6aSKevin Tung     if (configuration->configId == 0)
92*3f2f3e6aSKevin Tung     {
93*3f2f3e6aSKevin Tung         error("Image verification failed - missing config ID");
94*3f2f3e6aSKevin Tung         co_return false;
95*3f2f3e6aSKevin Tung     }
96*3f2f3e6aSKevin Tung 
97*3f2f3e6aSKevin Tung     co_return true;
98*3f2f3e6aSKevin Tung }
99*3f2f3e6aSKevin Tung 
checkId(PMBusCmd idCmd,uint32_t expected)100*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::checkId(PMBusCmd idCmd, uint32_t expected)
101*3f2f3e6aSKevin Tung {
102*3f2f3e6aSKevin Tung     constexpr size_t idLength = 2;
103*3f2f3e6aSKevin Tung 
104*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
105*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
106*3f2f3e6aSKevin Tung 
107*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
108*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
109*3f2f3e6aSKevin Tung     {
110*3f2f3e6aSKevin Tung         error("Failed to set page 0 for ID check");
111*3f2f3e6aSKevin Tung         co_return false;
112*3f2f3e6aSKevin Tung     }
113*3f2f3e6aSKevin Tung 
114*3f2f3e6aSKevin Tung     tbuf = buildByteVector(idCmd);
115*3f2f3e6aSKevin Tung     rbuf.resize(statusByteLength + idLength);
116*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
117*3f2f3e6aSKevin Tung     {
118*3f2f3e6aSKevin Tung         error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex,
119*3f2f3e6aSKevin Tung               static_cast<uint8_t>(idCmd));
120*3f2f3e6aSKevin Tung         co_return false;
121*3f2f3e6aSKevin Tung     }
122*3f2f3e6aSKevin Tung 
123*3f2f3e6aSKevin Tung     auto idBytes = std::span(rbuf).subspan(statusByteLength);
124*3f2f3e6aSKevin Tung     auto id = bytesToInt<uint32_t>(idBytes);
125*3f2f3e6aSKevin Tung 
126*3f2f3e6aSKevin Tung     if (id != expected)
127*3f2f3e6aSKevin Tung     {
128*3f2f3e6aSKevin Tung         error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
129*3f2f3e6aSKevin Tung               lg2::hex, static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP",
130*3f2f3e6aSKevin Tung               lg2::hex, expected);
131*3f2f3e6aSKevin Tung         co_return false;
132*3f2f3e6aSKevin Tung     }
133*3f2f3e6aSKevin Tung 
134*3f2f3e6aSKevin Tung     co_return true;
135*3f2f3e6aSKevin Tung }
136*3f2f3e6aSKevin Tung 
isPasswordUnlock()137*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::isPasswordUnlock()
138*3f2f3e6aSKevin Tung {
139*3f2f3e6aSKevin Tung     constexpr uint8_t passwordMatchMask = 0x08;
140*3f2f3e6aSKevin Tung 
141*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
142*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
143*3f2f3e6aSKevin Tung 
144*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
145*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
146*3f2f3e6aSKevin Tung     {
147*3f2f3e6aSKevin Tung         error("Failed to set page 0 for password unlock check");
148*3f2f3e6aSKevin Tung         co_return false;
149*3f2f3e6aSKevin Tung     }
150*3f2f3e6aSKevin Tung 
151*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::statusCML);
152*3f2f3e6aSKevin Tung     rbuf.resize(statusByteLength);
153*3f2f3e6aSKevin Tung 
154*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
155*3f2f3e6aSKevin Tung     {
156*3f2f3e6aSKevin Tung         error("Failed to check password unlock status");
157*3f2f3e6aSKevin Tung         co_return false;
158*3f2f3e6aSKevin Tung     }
159*3f2f3e6aSKevin Tung 
160*3f2f3e6aSKevin Tung     auto unlocked = (rbuf[0] & passwordMatchMask) != 0;
161*3f2f3e6aSKevin Tung 
162*3f2f3e6aSKevin Tung     debug("Password unlock status: {STATUS}", "STATUS",
163*3f2f3e6aSKevin Tung           unlocked ? "Unlocked" : "Locked");
164*3f2f3e6aSKevin Tung 
165*3f2f3e6aSKevin Tung     co_return unlocked;
166*3f2f3e6aSKevin Tung }
167*3f2f3e6aSKevin Tung 
unlockWriteProtect()168*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::unlockWriteProtect()
169*3f2f3e6aSKevin Tung {
170*3f2f3e6aSKevin Tung     constexpr uint8_t writeProtectModeMask = 0x04;
171*3f2f3e6aSKevin Tung     constexpr uint8_t unlockMemoryProtect = 0x00;
172*3f2f3e6aSKevin Tung     constexpr uint8_t unlockMTPProtect = 0x63;
173*3f2f3e6aSKevin Tung 
174*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
175*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf(statusByteLength);
176*3f2f3e6aSKevin Tung 
177*3f2f3e6aSKevin Tung     // Get write protection mode
178*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
179*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
180*3f2f3e6aSKevin Tung     {
181*3f2f3e6aSKevin Tung         error("Failed to set page 1 to check write protection mode");
182*3f2f3e6aSKevin Tung         co_return false;
183*3f2f3e6aSKevin Tung     }
184*3f2f3e6aSKevin Tung 
185*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::writeProtectMode);
186*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
187*3f2f3e6aSKevin Tung     {
188*3f2f3e6aSKevin Tung         error("Failed to get write protect mode");
189*3f2f3e6aSKevin Tung         co_return false;
190*3f2f3e6aSKevin Tung     }
191*3f2f3e6aSKevin Tung 
192*3f2f3e6aSKevin Tung     auto isMTPMode = (rbuf[0] & writeProtectModeMask) == 0;
193*3f2f3e6aSKevin Tung     auto unlockData = isMTPMode ? unlockMTPProtect : unlockMemoryProtect;
194*3f2f3e6aSKevin Tung 
195*3f2f3e6aSKevin Tung     // Unlock write protection
196*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
197*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
198*3f2f3e6aSKevin Tung     {
199*3f2f3e6aSKevin Tung         error("Failed to set page 0 to unlock write protection");
200*3f2f3e6aSKevin Tung         co_return false;
201*3f2f3e6aSKevin Tung     }
202*3f2f3e6aSKevin Tung 
203*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData);
204*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
205*3f2f3e6aSKevin Tung     {
206*3f2f3e6aSKevin Tung         error("Failed to unlock write protection");
207*3f2f3e6aSKevin Tung         co_return false;
208*3f2f3e6aSKevin Tung     }
209*3f2f3e6aSKevin Tung 
210*3f2f3e6aSKevin Tung     debug("Unlocked {MODE} write protection", "MODE",
211*3f2f3e6aSKevin Tung           isMTPMode ? "MTP" : "memory");
212*3f2f3e6aSKevin Tung     co_return true;
213*3f2f3e6aSKevin Tung }
214*3f2f3e6aSKevin Tung 
programPageRegisters(MPSPage page,const std::map<uint8_t,std::vector<MPSData>> & groupedData)215*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::programPageRegisters(
216*3f2f3e6aSKevin Tung     MPSPage page, const std::map<uint8_t, std::vector<MPSData>>& groupedData)
217*3f2f3e6aSKevin Tung {
218*3f2f3e6aSKevin Tung     auto pageNum = static_cast<uint8_t>(page);
219*3f2f3e6aSKevin Tung 
220*3f2f3e6aSKevin Tung     if (groupedData.find(pageNum) == groupedData.end())
221*3f2f3e6aSKevin Tung     {
222*3f2f3e6aSKevin Tung         debug("No data found for page {PAGE}", "PAGE", pageNum);
223*3f2f3e6aSKevin Tung         co_return true;
224*3f2f3e6aSKevin Tung     }
225*3f2f3e6aSKevin Tung 
226*3f2f3e6aSKevin Tung     const auto& data = groupedData.at(pageNum);
227*3f2f3e6aSKevin Tung 
228*3f2f3e6aSKevin Tung     // Page 2 is the multi-config page. Enter page2A to write config value
229*3f2f3e6aSKevin Tung     // to MTP space directly during multi-config page programming.
230*3f2f3e6aSKevin Tung     if (page == MPSPage::page2)
231*3f2f3e6aSKevin Tung     {
232*3f2f3e6aSKevin Tung         page = MPSPage::page2A;
233*3f2f3e6aSKevin Tung         pageNum = static_cast<uint8_t>(page);
234*3f2f3e6aSKevin Tung     }
235*3f2f3e6aSKevin Tung 
236*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
237*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
238*3f2f3e6aSKevin Tung 
239*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, page);
240*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
241*3f2f3e6aSKevin Tung     {
242*3f2f3e6aSKevin Tung         error("Failed to set page {PAGE} to program registers", "PAGE",
243*3f2f3e6aSKevin Tung               pageNum);
244*3f2f3e6aSKevin Tung         co_return false;
245*3f2f3e6aSKevin Tung     }
246*3f2f3e6aSKevin Tung 
247*3f2f3e6aSKevin Tung     auto i2cWriteWithRetry =
248*3f2f3e6aSKevin Tung         [&](const std::vector<uint8_t>& tbuf) -> sdbusplus::async::task<bool> {
249*3f2f3e6aSKevin Tung         std::vector<uint8_t> rbuf;
250*3f2f3e6aSKevin Tung         constexpr size_t maxRetries = 3;
251*3f2f3e6aSKevin Tung         constexpr auto retryDelay = std::chrono::milliseconds(10);
252*3f2f3e6aSKevin Tung 
253*3f2f3e6aSKevin Tung         for (size_t i = 1; i <= maxRetries; ++i)
254*3f2f3e6aSKevin Tung         {
255*3f2f3e6aSKevin Tung             if (i2cInterface.sendReceive(tbuf, rbuf))
256*3f2f3e6aSKevin Tung             {
257*3f2f3e6aSKevin Tung                 co_return true;
258*3f2f3e6aSKevin Tung             }
259*3f2f3e6aSKevin Tung             error("I2C write failed, retry {RETRY}", "RETRY", i);
260*3f2f3e6aSKevin Tung             co_await sdbusplus::async::sleep_for(ctx, retryDelay);
261*3f2f3e6aSKevin Tung         }
262*3f2f3e6aSKevin Tung         co_return false;
263*3f2f3e6aSKevin Tung     };
264*3f2f3e6aSKevin Tung 
265*3f2f3e6aSKevin Tung     for (const auto& regData : data)
266*3f2f3e6aSKevin Tung     {
267*3f2f3e6aSKevin Tung         tbuf = {regData.addr};
268*3f2f3e6aSKevin Tung         tbuf.insert(tbuf.end(), regData.data.begin(),
269*3f2f3e6aSKevin Tung                     regData.data.begin() + regData.length);
270*3f2f3e6aSKevin Tung 
271*3f2f3e6aSKevin Tung         if (!(co_await i2cWriteWithRetry(tbuf)))
272*3f2f3e6aSKevin Tung         {
273*3f2f3e6aSKevin Tung             error(
274*3f2f3e6aSKevin Tung                 "Failed to write data {DATA} to register {REG} on page {PAGE}",
275*3f2f3e6aSKevin Tung                 "DATA", lg2::hex, bytesToInt<uint32_t>(regData.data), "REG",
276*3f2f3e6aSKevin Tung                 lg2::hex, regData.addr, "PAGE", pageNum);
277*3f2f3e6aSKevin Tung             co_return false;
278*3f2f3e6aSKevin Tung         }
279*3f2f3e6aSKevin Tung 
280*3f2f3e6aSKevin Tung         if (page == MPSPage::page2A)
281*3f2f3e6aSKevin Tung         {
282*3f2f3e6aSKevin Tung             // Page 2A requires a delay after each register write
283*3f2f3e6aSKevin Tung             co_await sdbusplus::async::sleep_for(ctx,
284*3f2f3e6aSKevin Tung                                                  std::chrono::milliseconds(2));
285*3f2f3e6aSKevin Tung         }
286*3f2f3e6aSKevin Tung     }
287*3f2f3e6aSKevin Tung 
288*3f2f3e6aSKevin Tung     debug("Programmed {N} registers on page {PAGE}", "N", data.size(), "PAGE",
289*3f2f3e6aSKevin Tung           pageNum);
290*3f2f3e6aSKevin Tung 
291*3f2f3e6aSKevin Tung     co_return true;
292*3f2f3e6aSKevin Tung }
293*3f2f3e6aSKevin Tung 
storeDataIntoMTP()294*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::storeDataIntoMTP()
295*3f2f3e6aSKevin Tung {
296*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
297*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
298*3f2f3e6aSKevin Tung 
299*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
300*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
301*3f2f3e6aSKevin Tung     {
302*3f2f3e6aSKevin Tung         error("Failed to set page 0 for storing data into MTP");
303*3f2f3e6aSKevin Tung         co_return false;
304*3f2f3e6aSKevin Tung     }
305*3f2f3e6aSKevin Tung 
306*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::storeDataIntoMTP);
307*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
308*3f2f3e6aSKevin Tung     {
309*3f2f3e6aSKevin Tung         error("Failed to store data into MTP");
310*3f2f3e6aSKevin Tung         co_return false;
311*3f2f3e6aSKevin Tung     }
312*3f2f3e6aSKevin Tung 
313*3f2f3e6aSKevin Tung     // Wait store data into MTP
314*3f2f3e6aSKevin Tung     co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
315*3f2f3e6aSKevin Tung 
316*3f2f3e6aSKevin Tung     debug("Stored data into MTP");
317*3f2f3e6aSKevin Tung 
318*3f2f3e6aSKevin Tung     co_return true;
319*3f2f3e6aSKevin Tung }
320*3f2f3e6aSKevin Tung 
enableMTPPageWriteRead()321*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::enableMTPPageWriteRead()
322*3f2f3e6aSKevin Tung {
323*3f2f3e6aSKevin Tung     constexpr uint8_t mtpByteRWEnable = 0x20;
324*3f2f3e6aSKevin Tung 
325*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
326*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
327*3f2f3e6aSKevin Tung 
328*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
329*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
330*3f2f3e6aSKevin Tung     {
331*3f2f3e6aSKevin Tung         error("Failed to set page 1 to enable MTP page write/read");
332*3f2f3e6aSKevin Tung         co_return false;
333*3f2f3e6aSKevin Tung     }
334*3f2f3e6aSKevin Tung 
335*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::enableMTPPageWR);
336*3f2f3e6aSKevin Tung     rbuf.resize(statusByteLength);
337*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
338*3f2f3e6aSKevin Tung     {
339*3f2f3e6aSKevin Tung         error("Failed to read MTP page write/read status");
340*3f2f3e6aSKevin Tung         co_return false;
341*3f2f3e6aSKevin Tung     }
342*3f2f3e6aSKevin Tung 
343*3f2f3e6aSKevin Tung     uint8_t enableMTPPageWRData = rbuf[0] | mtpByteRWEnable;
344*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::enableMTPPageWR, enableMTPPageWRData);
345*3f2f3e6aSKevin Tung     rbuf.resize(0);
346*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
347*3f2f3e6aSKevin Tung     {
348*3f2f3e6aSKevin Tung         error("Failed to enable MTP page write/read");
349*3f2f3e6aSKevin Tung         co_return false;
350*3f2f3e6aSKevin Tung     }
351*3f2f3e6aSKevin Tung 
352*3f2f3e6aSKevin Tung     debug("Enabled MTP page write/read");
353*3f2f3e6aSKevin Tung     co_return true;
354*3f2f3e6aSKevin Tung }
355*3f2f3e6aSKevin Tung 
enableMultiConfigCRC()356*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::enableMultiConfigCRC()
357*3f2f3e6aSKevin Tung {
358*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
359*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
360*3f2f3e6aSKevin Tung 
361*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
362*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
363*3f2f3e6aSKevin Tung     {
364*3f2f3e6aSKevin Tung         error("Failed to set page 2 to enable multi-config CRC");
365*3f2f3e6aSKevin Tung         co_return false;
366*3f2f3e6aSKevin Tung     }
367*3f2f3e6aSKevin Tung 
368*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::enableMultiConfigCRC);
369*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
370*3f2f3e6aSKevin Tung     {
371*3f2f3e6aSKevin Tung         error("Failed to enable multi-config CRC");
372*3f2f3e6aSKevin Tung         co_return false;
373*3f2f3e6aSKevin Tung     }
374*3f2f3e6aSKevin Tung 
375*3f2f3e6aSKevin Tung     debug("Enabled multi-config CRC");
376*3f2f3e6aSKevin Tung     co_return true;
377*3f2f3e6aSKevin Tung }
378*3f2f3e6aSKevin Tung 
getCRC(uint32_t * checksum)379*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::getCRC(uint32_t* checksum)
380*3f2f3e6aSKevin Tung {
381*3f2f3e6aSKevin Tung     if (checksum == nullptr)
382*3f2f3e6aSKevin Tung     {
383*3f2f3e6aSKevin Tung         error("getCRC() called with null checksum pointer");
384*3f2f3e6aSKevin Tung         co_return false;
385*3f2f3e6aSKevin Tung     }
386*3f2f3e6aSKevin Tung 
387*3f2f3e6aSKevin Tung     constexpr size_t crcLength = 2;
388*3f2f3e6aSKevin Tung 
389*3f2f3e6aSKevin Tung     std::vector<uint8_t> tbuf;
390*3f2f3e6aSKevin Tung     std::vector<uint8_t> rbuf;
391*3f2f3e6aSKevin Tung 
392*3f2f3e6aSKevin Tung     uint16_t userCodeCRC = 0;
393*3f2f3e6aSKevin Tung     uint16_t multiConfigCRC = 0;
394*3f2f3e6aSKevin Tung 
395*3f2f3e6aSKevin Tung     // Read User Code CRC
396*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page29);
397*3f2f3e6aSKevin Tung     rbuf.resize(0);
398*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
399*3f2f3e6aSKevin Tung     {
400*3f2f3e6aSKevin Tung         error("Failed to set page 29 for User Code CRC read");
401*3f2f3e6aSKevin Tung         co_return false;
402*3f2f3e6aSKevin Tung     }
403*3f2f3e6aSKevin Tung 
404*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::readUserCodeCRC);
405*3f2f3e6aSKevin Tung     rbuf.resize(crcLength);
406*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
407*3f2f3e6aSKevin Tung     {
408*3f2f3e6aSKevin Tung         error("Failed to read User Code CRC from device");
409*3f2f3e6aSKevin Tung         co_return false;
410*3f2f3e6aSKevin Tung     }
411*3f2f3e6aSKevin Tung 
412*3f2f3e6aSKevin Tung     userCodeCRC = bytesToInt<uint16_t>(rbuf);
413*3f2f3e6aSKevin Tung 
414*3f2f3e6aSKevin Tung     // Read Multi Config CRC
415*3f2f3e6aSKevin Tung     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2A);
416*3f2f3e6aSKevin Tung     rbuf.resize(0);
417*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
418*3f2f3e6aSKevin Tung     {
419*3f2f3e6aSKevin Tung         error("Failed to set page 2A for Multi Config CRC read");
420*3f2f3e6aSKevin Tung         co_return false;
421*3f2f3e6aSKevin Tung     }
422*3f2f3e6aSKevin Tung 
423*3f2f3e6aSKevin Tung     tbuf = buildByteVector(MP297XCmd::readMultiConfigCRC);
424*3f2f3e6aSKevin Tung     rbuf.resize(crcLength);
425*3f2f3e6aSKevin Tung     if (!i2cInterface.sendReceive(tbuf, rbuf))
426*3f2f3e6aSKevin Tung     {
427*3f2f3e6aSKevin Tung         error("Failed to read Multi Config CRC from device");
428*3f2f3e6aSKevin Tung         co_return false;
429*3f2f3e6aSKevin Tung     }
430*3f2f3e6aSKevin Tung 
431*3f2f3e6aSKevin Tung     multiConfigCRC = bytesToInt<uint16_t>(rbuf);
432*3f2f3e6aSKevin Tung 
433*3f2f3e6aSKevin Tung     // Combine: [byte3 byte2] = userCodeCRC, [byte1 byte0] = multiConfigCRC
434*3f2f3e6aSKevin Tung     *checksum = (static_cast<uint32_t>(userCodeCRC) << 16) |
435*3f2f3e6aSKevin Tung                 static_cast<uint32_t>(multiConfigCRC);
436*3f2f3e6aSKevin Tung 
437*3f2f3e6aSKevin Tung     debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
438*3f2f3e6aSKevin Tung 
439*3f2f3e6aSKevin Tung     co_return true;
440*3f2f3e6aSKevin Tung }
441*3f2f3e6aSKevin Tung 
checkMTPCRC()442*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::checkMTPCRC()
443*3f2f3e6aSKevin Tung {
444*3f2f3e6aSKevin Tung     uint32_t crc = 0;
445*3f2f3e6aSKevin Tung     auto expectedCRC = (configuration->crcUser << 16) | configuration->crcMulti;
446*3f2f3e6aSKevin Tung 
447*3f2f3e6aSKevin Tung     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
448*3f2f3e6aSKevin Tung     if (!co_await getCRC(&crc))
449*3f2f3e6aSKevin Tung     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
450*3f2f3e6aSKevin Tung     {
451*3f2f3e6aSKevin Tung         error("Failed to get CRC for MTP check");
452*3f2f3e6aSKevin Tung         co_return false;
453*3f2f3e6aSKevin Tung     }
454*3f2f3e6aSKevin Tung 
455*3f2f3e6aSKevin Tung     debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
456*3f2f3e6aSKevin Tung           lg2::hex, expectedCRC);
457*3f2f3e6aSKevin Tung 
458*3f2f3e6aSKevin Tung     co_return crc == expectedCRC;
459*3f2f3e6aSKevin Tung }
460*3f2f3e6aSKevin Tung 
forcedUpdateAllowed()461*3f2f3e6aSKevin Tung bool MP297X::forcedUpdateAllowed()
462*3f2f3e6aSKevin Tung {
463*3f2f3e6aSKevin Tung     return true;
464*3f2f3e6aSKevin Tung }
465*3f2f3e6aSKevin Tung 
updateFirmware(bool force)466*3f2f3e6aSKevin Tung sdbusplus::async::task<bool> MP297X::updateFirmware(bool force)
467*3f2f3e6aSKevin Tung {
468*3f2f3e6aSKevin Tung     (void)force;
469*3f2f3e6aSKevin Tung 
470*3f2f3e6aSKevin Tung     auto groupedConfigData = getGroupedConfigData();
471*3f2f3e6aSKevin Tung 
472*3f2f3e6aSKevin Tung     if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId))
473*3f2f3e6aSKevin Tung     {
474*3f2f3e6aSKevin Tung         co_return false;
475*3f2f3e6aSKevin Tung     }
476*3f2f3e6aSKevin Tung 
477*3f2f3e6aSKevin Tung     if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId))
478*3f2f3e6aSKevin Tung     {
479*3f2f3e6aSKevin Tung         co_return false;
480*3f2f3e6aSKevin Tung     }
481*3f2f3e6aSKevin Tung 
482*3f2f3e6aSKevin Tung     if (!co_await isPasswordUnlock())
483*3f2f3e6aSKevin Tung     {
484*3f2f3e6aSKevin Tung         co_return false;
485*3f2f3e6aSKevin Tung     }
486*3f2f3e6aSKevin Tung 
487*3f2f3e6aSKevin Tung     if (!co_await unlockWriteProtect())
488*3f2f3e6aSKevin Tung     {
489*3f2f3e6aSKevin Tung         co_return false;
490*3f2f3e6aSKevin Tung     }
491*3f2f3e6aSKevin Tung 
492*3f2f3e6aSKevin Tung     if (!co_await programPageRegisters(MPSPage::page0, groupedConfigData))
493*3f2f3e6aSKevin Tung     {
494*3f2f3e6aSKevin Tung         co_return false;
495*3f2f3e6aSKevin Tung     }
496*3f2f3e6aSKevin Tung 
497*3f2f3e6aSKevin Tung     if (!co_await programPageRegisters(MPSPage::page1, groupedConfigData))
498*3f2f3e6aSKevin Tung     {
499*3f2f3e6aSKevin Tung         co_return false;
500*3f2f3e6aSKevin Tung     }
501*3f2f3e6aSKevin Tung 
502*3f2f3e6aSKevin Tung     if (!co_await storeDataIntoMTP())
503*3f2f3e6aSKevin Tung     {
504*3f2f3e6aSKevin Tung         co_return false;
505*3f2f3e6aSKevin Tung     }
506*3f2f3e6aSKevin Tung 
507*3f2f3e6aSKevin Tung     if (!co_await enableMTPPageWriteRead())
508*3f2f3e6aSKevin Tung     {
509*3f2f3e6aSKevin Tung         co_return false;
510*3f2f3e6aSKevin Tung     }
511*3f2f3e6aSKevin Tung 
512*3f2f3e6aSKevin Tung     if (!co_await enableMultiConfigCRC())
513*3f2f3e6aSKevin Tung     {
514*3f2f3e6aSKevin Tung         co_return false;
515*3f2f3e6aSKevin Tung     }
516*3f2f3e6aSKevin Tung 
517*3f2f3e6aSKevin Tung     if (!co_await programPageRegisters(MPSPage::page2, groupedConfigData))
518*3f2f3e6aSKevin Tung     {
519*3f2f3e6aSKevin Tung         co_return false;
520*3f2f3e6aSKevin Tung     }
521*3f2f3e6aSKevin Tung 
522*3f2f3e6aSKevin Tung     if (!(co_await checkMTPCRC()))
523*3f2f3e6aSKevin Tung     {
524*3f2f3e6aSKevin Tung         co_return false;
525*3f2f3e6aSKevin Tung     }
526*3f2f3e6aSKevin Tung 
527*3f2f3e6aSKevin Tung     co_return true;
528*3f2f3e6aSKevin Tung }
529*3f2f3e6aSKevin Tung 
530*3f2f3e6aSKevin Tung } // namespace phosphor::software::VR
531