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