1*f730973bSKevin Tung #include "mpx9xx.hpp"
2*f730973bSKevin Tung
3*f730973bSKevin Tung #include "common/include/utils.hpp"
4*f730973bSKevin Tung
5*f730973bSKevin Tung #include <phosphor-logging/lg2.hpp>
6*f730973bSKevin Tung
7*f730973bSKevin Tung PHOSPHOR_LOG2_USING;
8*f730973bSKevin Tung
9*f730973bSKevin Tung namespace phosphor::software::VR
10*f730973bSKevin Tung {
11*f730973bSKevin Tung
12*f730973bSKevin Tung static constexpr std::string_view vendorIdRegName = "VENDOR_ID_VR";
13*f730973bSKevin Tung static constexpr std::string_view mfrDeviceIDCFGRegName = "MFR_DEVICE_ID_CFG";
14*f730973bSKevin Tung static constexpr std::string_view crcUserMultiRegName = "CRC_USER_MULTI";
15*f730973bSKevin Tung
16*f730973bSKevin Tung static constexpr uint8_t pageMask = 0x0F;
17*f730973bSKevin Tung
18*f730973bSKevin Tung enum class MPX9XXCmd : uint8_t
19*f730973bSKevin Tung {
20*f730973bSKevin Tung // Page 0 commands
21*f730973bSKevin Tung storeUserAll = 0x15,
22*f730973bSKevin Tung userData08 = 0xB8,
23*f730973bSKevin Tung
24*f730973bSKevin Tung // Page 2 commands
25*f730973bSKevin Tung configIdMP292X = 0xA9,
26*f730973bSKevin Tung mfrMulconfigSel = 0xAB,
27*f730973bSKevin Tung configIdMP994X = 0xAF,
28*f730973bSKevin Tung mfrNVMPmbusCtrl = 0xCA,
29*f730973bSKevin Tung mfrDebug = 0xD4,
30*f730973bSKevin Tung deviceId = 0xDB,
31*f730973bSKevin Tung
32*f730973bSKevin Tung // Page 5 commands
33*f730973bSKevin Tung vendorId = 0xBA,
34*f730973bSKevin Tung
35*f730973bSKevin Tung // Page 7 commands
36*f730973bSKevin Tung storeFaultTrigger = 0x51,
37*f730973bSKevin Tung };
38*f730973bSKevin Tung
getConfigIdCmd() const39*f730973bSKevin Tung MPX9XXCmd MP292X::getConfigIdCmd() const
40*f730973bSKevin Tung {
41*f730973bSKevin Tung return MPX9XXCmd::configIdMP292X;
42*f730973bSKevin Tung }
43*f730973bSKevin Tung
getConfigIdCmd() const44*f730973bSKevin Tung MPX9XXCmd MP994X::getConfigIdCmd() const
45*f730973bSKevin Tung {
46*f730973bSKevin Tung return MPX9XXCmd::configIdMP994X;
47*f730973bSKevin Tung }
48*f730973bSKevin Tung
parseDeviceConfiguration()49*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::parseDeviceConfiguration()
50*f730973bSKevin Tung {
51*f730973bSKevin Tung if (!configuration)
52*f730973bSKevin Tung {
53*f730973bSKevin Tung error("Device configuration not initialized");
54*f730973bSKevin Tung co_return false;
55*f730973bSKevin Tung }
56*f730973bSKevin Tung
57*f730973bSKevin Tung for (const auto& tokens : parser->lineTokens)
58*f730973bSKevin Tung {
59*f730973bSKevin Tung if (!parser->isValidDataTokens(tokens))
60*f730973bSKevin Tung {
61*f730973bSKevin Tung continue;
62*f730973bSKevin Tung }
63*f730973bSKevin Tung
64*f730973bSKevin Tung auto regName = parser->getVal<std::string>(tokens, ATE::regName);
65*f730973bSKevin Tung
66*f730973bSKevin Tung if (regName == vendorIdRegName)
67*f730973bSKevin Tung {
68*f730973bSKevin Tung configuration->vendorId =
69*f730973bSKevin Tung parser->getVal<uint32_t>(tokens, ATE::regDataHex);
70*f730973bSKevin Tung configuration->configId =
71*f730973bSKevin Tung parser->getVal<uint32_t>(tokens, ATE::configId);
72*f730973bSKevin Tung }
73*f730973bSKevin Tung else if (regName == mfrDeviceIDCFGRegName)
74*f730973bSKevin Tung {
75*f730973bSKevin Tung configuration->productId =
76*f730973bSKevin Tung parser->getVal<uint32_t>(tokens, ATE::regDataHex);
77*f730973bSKevin Tung }
78*f730973bSKevin Tung else if (regName == crcUserMultiRegName)
79*f730973bSKevin Tung {
80*f730973bSKevin Tung configuration->crcMulti =
81*f730973bSKevin Tung parser->getVal<uint32_t>(tokens, ATE::regDataHex);
82*f730973bSKevin Tung break;
83*f730973bSKevin Tung }
84*f730973bSKevin Tung }
85*f730973bSKevin Tung
86*f730973bSKevin Tung co_return true;
87*f730973bSKevin Tung }
88*f730973bSKevin Tung
verifyImage(const uint8_t * image,size_t imageSize)89*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::verifyImage(const uint8_t* image,
90*f730973bSKevin Tung size_t imageSize)
91*f730973bSKevin Tung {
92*f730973bSKevin Tung if (!co_await parseImage(image, imageSize, MPSImageType::type1))
93*f730973bSKevin Tung {
94*f730973bSKevin Tung error("Image verification failed: image parsing failed");
95*f730973bSKevin Tung co_return false;
96*f730973bSKevin Tung }
97*f730973bSKevin Tung
98*f730973bSKevin Tung if (configuration->registersData.empty())
99*f730973bSKevin Tung {
100*f730973bSKevin Tung error("Image verification failed: no data found");
101*f730973bSKevin Tung co_return false;
102*f730973bSKevin Tung }
103*f730973bSKevin Tung
104*f730973bSKevin Tung if (configuration->vendorId == 0 || configuration->productId == 0 ||
105*f730973bSKevin Tung configuration->configId == 0)
106*f730973bSKevin Tung {
107*f730973bSKevin Tung error("Image verification failed: missing required field "
108*f730973bSKevin Tung "vendor ID, product ID, or config ID");
109*f730973bSKevin Tung co_return false;
110*f730973bSKevin Tung }
111*f730973bSKevin Tung
112*f730973bSKevin Tung co_return true;
113*f730973bSKevin Tung }
114*f730973bSKevin Tung
checkId(MPX9XXCmd idCmd,uint32_t expected)115*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::checkId(MPX9XXCmd idCmd, uint32_t expected)
116*f730973bSKevin Tung {
117*f730973bSKevin Tung static constexpr size_t vendorIdLength = 2;
118*f730973bSKevin Tung static constexpr size_t productIdLength = 1;
119*f730973bSKevin Tung static constexpr size_t configIdLength = 2;
120*f730973bSKevin Tung
121*f730973bSKevin Tung MPSPage page;
122*f730973bSKevin Tung size_t idLen = 0;
123*f730973bSKevin Tung const uint8_t cmd = static_cast<uint8_t>(idCmd);
124*f730973bSKevin Tung
125*f730973bSKevin Tung switch (idCmd)
126*f730973bSKevin Tung {
127*f730973bSKevin Tung case MPX9XXCmd::vendorId:
128*f730973bSKevin Tung page = MPSPage::page5;
129*f730973bSKevin Tung idLen = vendorIdLength;
130*f730973bSKevin Tung break;
131*f730973bSKevin Tung case MPX9XXCmd::deviceId:
132*f730973bSKevin Tung page = MPSPage::page2;
133*f730973bSKevin Tung idLen = productIdLength;
134*f730973bSKevin Tung break;
135*f730973bSKevin Tung case MPX9XXCmd::configIdMP292X:
136*f730973bSKevin Tung case MPX9XXCmd::configIdMP994X:
137*f730973bSKevin Tung page = MPSPage::page2;
138*f730973bSKevin Tung idLen = configIdLength;
139*f730973bSKevin Tung break;
140*f730973bSKevin Tung default:
141*f730973bSKevin Tung error("Invalid command for ID check: {CMD}", "CMD", lg2::hex, cmd);
142*f730973bSKevin Tung co_return false;
143*f730973bSKevin Tung }
144*f730973bSKevin Tung
145*f730973bSKevin Tung std::vector<uint8_t> tbuf;
146*f730973bSKevin Tung std::vector<uint8_t> rbuf;
147*f730973bSKevin Tung
148*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, page);
149*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
150*f730973bSKevin Tung {
151*f730973bSKevin Tung error("Failed to set page for ID check");
152*f730973bSKevin Tung co_return false;
153*f730973bSKevin Tung }
154*f730973bSKevin Tung
155*f730973bSKevin Tung tbuf = buildByteVector(idCmd);
156*f730973bSKevin Tung rbuf.resize(idLen);
157*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
158*f730973bSKevin Tung {
159*f730973bSKevin Tung error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex, cmd);
160*f730973bSKevin Tung co_return false;
161*f730973bSKevin Tung }
162*f730973bSKevin Tung
163*f730973bSKevin Tung auto id = bytesToInt<uint32_t>(rbuf);
164*f730973bSKevin Tung
165*f730973bSKevin Tung if (id != expected)
166*f730973bSKevin Tung {
167*f730973bSKevin Tung error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
168*f730973bSKevin Tung lg2::hex, cmd, "ID", lg2::hex, id, "EXP", lg2::hex, expected);
169*f730973bSKevin Tung co_return false;
170*f730973bSKevin Tung }
171*f730973bSKevin Tung
172*f730973bSKevin Tung co_return true;
173*f730973bSKevin Tung }
174*f730973bSKevin Tung
unlockWriteProtect()175*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::unlockWriteProtect()
176*f730973bSKevin Tung {
177*f730973bSKevin Tung static constexpr uint8_t unlockWriteProtectData = 0x00;
178*f730973bSKevin Tung
179*f730973bSKevin Tung std::vector<uint8_t> tbuf;
180*f730973bSKevin Tung std::vector<uint8_t> rbuf;
181*f730973bSKevin Tung
182*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
183*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
184*f730973bSKevin Tung {
185*f730973bSKevin Tung error("Failed to set page 0 to unlock write protection mode");
186*f730973bSKevin Tung co_return false;
187*f730973bSKevin Tung }
188*f730973bSKevin Tung
189*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::writeProtect, unlockWriteProtectData);
190*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
191*f730973bSKevin Tung {
192*f730973bSKevin Tung error("Failed to unlock write protect mode");
193*f730973bSKevin Tung co_return false;
194*f730973bSKevin Tung }
195*f730973bSKevin Tung
196*f730973bSKevin Tung debug("Write protection unlocked");
197*f730973bSKevin Tung co_return true;
198*f730973bSKevin Tung }
199*f730973bSKevin Tung
disableStoreFaultTriggering()200*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::disableStoreFaultTriggering()
201*f730973bSKevin Tung {
202*f730973bSKevin Tung static constexpr size_t mfrDebugDataLength = 2;
203*f730973bSKevin Tung static constexpr uint16_t enableEnteringPage7Mask = 0x8000;
204*f730973bSKevin Tung static constexpr uint16_t disableStoreFaultTriggeringData = 0x1000;
205*f730973bSKevin Tung
206*f730973bSKevin Tung std::vector<uint8_t> tbuf;
207*f730973bSKevin Tung std::vector<uint8_t> rbuf;
208*f730973bSKevin Tung
209*f730973bSKevin Tung // enable entering page 7
210*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
211*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
212*f730973bSKevin Tung {
213*f730973bSKevin Tung error("Failed to set page 2 to enable entering page 7");
214*f730973bSKevin Tung co_return false;
215*f730973bSKevin Tung }
216*f730973bSKevin Tung
217*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::mfrDebug);
218*f730973bSKevin Tung rbuf.resize(mfrDebugDataLength);
219*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
220*f730973bSKevin Tung {
221*f730973bSKevin Tung error("Failed to read MFR Debug register to enable entering 7");
222*f730973bSKevin Tung co_return false;
223*f730973bSKevin Tung }
224*f730973bSKevin Tung
225*f730973bSKevin Tung uint16_t data = (rbuf[1] << 8) | rbuf[0] | enableEnteringPage7Mask;
226*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::mfrDebug, data);
227*f730973bSKevin Tung rbuf.clear();
228*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
229*f730973bSKevin Tung {
230*f730973bSKevin Tung error("Failed to enable entering page 7");
231*f730973bSKevin Tung co_return false;
232*f730973bSKevin Tung }
233*f730973bSKevin Tung
234*f730973bSKevin Tung // disable store fault triggering
235*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page7);
236*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
237*f730973bSKevin Tung {
238*f730973bSKevin Tung error("Failed to set page 7 to disable store fault triggering");
239*f730973bSKevin Tung co_return false;
240*f730973bSKevin Tung }
241*f730973bSKevin Tung
242*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::storeFaultTrigger,
243*f730973bSKevin Tung disableStoreFaultTriggeringData);
244*f730973bSKevin Tung rbuf.clear();
245*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
246*f730973bSKevin Tung {
247*f730973bSKevin Tung error("Failed to disable store fault triggering");
248*f730973bSKevin Tung co_return false;
249*f730973bSKevin Tung }
250*f730973bSKevin Tung
251*f730973bSKevin Tung debug("Disabled store fault triggerring");
252*f730973bSKevin Tung
253*f730973bSKevin Tung co_return true;
254*f730973bSKevin Tung }
255*f730973bSKevin Tung
setMultiConfigAddress(uint8_t config)256*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::setMultiConfigAddress(uint8_t config)
257*f730973bSKevin Tung {
258*f730973bSKevin Tung // MPS994X: Select multi-configuration address
259*f730973bSKevin Tung // Write to Page 2 @ 0xAB:
260*f730973bSKevin Tung // - Bit[3] = MFR_MULCONFIG_SEL (1 = enable)
261*f730973bSKevin Tung // - Bit[2:0] = MFR_MULCONFIG_ADDR (0 ~ 7 → selects one of 8 configs)
262*f730973bSKevin Tung // Resulting values for config set 1 ~ 8: 0x08 ~ 0x0F
263*f730973bSKevin Tung
264*f730973bSKevin Tung auto addr = config - 1;
265*f730973bSKevin Tung
266*f730973bSKevin Tung static constexpr uint8_t maxMultiConfigAddr = 7;
267*f730973bSKevin Tung static constexpr uint8_t enableMultiConfigAddrSel = 0x08;
268*f730973bSKevin Tung
269*f730973bSKevin Tung if (addr > maxMultiConfigAddr)
270*f730973bSKevin Tung {
271*f730973bSKevin Tung error("Invalid multi config address: {ADDR}", "ADDR", addr);
272*f730973bSKevin Tung }
273*f730973bSKevin Tung
274*f730973bSKevin Tung std::vector<uint8_t> tbuf;
275*f730973bSKevin Tung std::vector<uint8_t> rbuf;
276*f730973bSKevin Tung
277*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
278*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
279*f730973bSKevin Tung {
280*f730973bSKevin Tung error("Failed to set page 2 to set multi config address");
281*f730973bSKevin Tung co_return false;
282*f730973bSKevin Tung }
283*f730973bSKevin Tung
284*f730973bSKevin Tung uint8_t selectAddrData = enableMultiConfigAddrSel + addr;
285*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::mfrMulconfigSel, selectAddrData);
286*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
287*f730973bSKevin Tung {
288*f730973bSKevin Tung error("Failed to write {DATA} to multi config select register {REG}",
289*f730973bSKevin Tung "DATA", lg2::hex, selectAddrData, "REG", lg2::hex,
290*f730973bSKevin Tung static_cast<uint8_t>(MPX9XXCmd::mfrMulconfigSel));
291*f730973bSKevin Tung co_return false;
292*f730973bSKevin Tung }
293*f730973bSKevin Tung
294*f730973bSKevin Tung debug("Selected multi config set address {ADDR}", "ADDR", addr);
295*f730973bSKevin Tung co_return true;
296*f730973bSKevin Tung }
297*f730973bSKevin Tung
programConfigData(const std::vector<MPSData> & gdata)298*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::programConfigData(
299*f730973bSKevin Tung const std::vector<MPSData>& gdata)
300*f730973bSKevin Tung {
301*f730973bSKevin Tung std::vector<uint8_t> tbuf;
302*f730973bSKevin Tung std::vector<uint8_t> rbuf;
303*f730973bSKevin Tung
304*f730973bSKevin Tung for (const auto& data : gdata)
305*f730973bSKevin Tung {
306*f730973bSKevin Tung uint8_t page = data.page & pageMask;
307*f730973bSKevin Tung
308*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, page);
309*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
310*f730973bSKevin Tung {
311*f730973bSKevin Tung error("Failed to set page {PAGE} for register {REG}", "PAGE", page,
312*f730973bSKevin Tung "REG", lg2::hex, data.addr);
313*f730973bSKevin Tung co_return false;
314*f730973bSKevin Tung }
315*f730973bSKevin Tung
316*f730973bSKevin Tung tbuf = {data.addr};
317*f730973bSKevin Tung tbuf.insert(tbuf.end(), data.data.begin(),
318*f730973bSKevin Tung data.data.begin() + data.length);
319*f730973bSKevin Tung
320*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
321*f730973bSKevin Tung {
322*f730973bSKevin Tung error(
323*f730973bSKevin Tung "Failed to write data {DATA} to register {REG} on page {PAGE}",
324*f730973bSKevin Tung "DATA", lg2::hex, bytesToInt<uint32_t>(data.data), "REG",
325*f730973bSKevin Tung lg2::hex, data.addr, "PAGE", page);
326*f730973bSKevin Tung co_return false;
327*f730973bSKevin Tung }
328*f730973bSKevin Tung }
329*f730973bSKevin Tung
330*f730973bSKevin Tung if (!co_await storeDataIntoMTP())
331*f730973bSKevin Tung {
332*f730973bSKevin Tung error("Failed to store code into MTP after programming config data");
333*f730973bSKevin Tung co_return false;
334*f730973bSKevin Tung }
335*f730973bSKevin Tung
336*f730973bSKevin Tung co_return true;
337*f730973bSKevin Tung }
338*f730973bSKevin Tung
programAllRegisters()339*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::programAllRegisters()
340*f730973bSKevin Tung {
341*f730973bSKevin Tung // config 0 = User Registers
342*f730973bSKevin Tung // config 1 ~ 8 = Multi-configuration Registers
343*f730973bSKevin Tung for (const auto& [config, gdata] : getGroupedConfigData(~pageMask, 4))
344*f730973bSKevin Tung {
345*f730973bSKevin Tung debug("Configuring registers for config set {SET}", "SET", config);
346*f730973bSKevin Tung
347*f730973bSKevin Tung if (config > 0)
348*f730973bSKevin Tung {
349*f730973bSKevin Tung if (!co_await setMultiConfigAddress(config))
350*f730973bSKevin Tung {
351*f730973bSKevin Tung co_return false;
352*f730973bSKevin Tung }
353*f730973bSKevin Tung }
354*f730973bSKevin Tung
355*f730973bSKevin Tung if (!co_await programConfigData(gdata))
356*f730973bSKevin Tung {
357*f730973bSKevin Tung error("Failed to program config set {SET}", "SET", config);
358*f730973bSKevin Tung co_return false;
359*f730973bSKevin Tung }
360*f730973bSKevin Tung
361*f730973bSKevin Tung debug("Configured {SIZE} registers for config set {SET}", "SIZE",
362*f730973bSKevin Tung gdata.size(), "SET", config);
363*f730973bSKevin Tung }
364*f730973bSKevin Tung
365*f730973bSKevin Tung debug("All registers were programmed successfully");
366*f730973bSKevin Tung
367*f730973bSKevin Tung co_return true;
368*f730973bSKevin Tung }
369*f730973bSKevin Tung
storeDataIntoMTP()370*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::storeDataIntoMTP()
371*f730973bSKevin Tung {
372*f730973bSKevin Tung std::vector<uint8_t> tbuf;
373*f730973bSKevin Tung std::vector<uint8_t> rbuf;
374*f730973bSKevin Tung
375*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
376*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
377*f730973bSKevin Tung {
378*f730973bSKevin Tung error("Failed to set page 0 to store data into MTP");
379*f730973bSKevin Tung co_return false;
380*f730973bSKevin Tung }
381*f730973bSKevin Tung
382*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::storeUserAll);
383*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
384*f730973bSKevin Tung {
385*f730973bSKevin Tung error("Failed to store data into MTP");
386*f730973bSKevin Tung co_return false;
387*f730973bSKevin Tung }
388*f730973bSKevin Tung
389*f730973bSKevin Tung // Wait store data
390*f730973bSKevin Tung co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(1000));
391*f730973bSKevin Tung
392*f730973bSKevin Tung debug("Stored data into MTP");
393*f730973bSKevin Tung co_return true;
394*f730973bSKevin Tung }
395*f730973bSKevin Tung
getCRC(uint32_t * checksum)396*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::getCRC(uint32_t* checksum)
397*f730973bSKevin Tung {
398*f730973bSKevin Tung static constexpr size_t crcUserMultiDataLength = 4;
399*f730973bSKevin Tung static constexpr size_t statusByteLength = 1;
400*f730973bSKevin Tung
401*f730973bSKevin Tung std::vector<uint8_t> tbuf;
402*f730973bSKevin Tung std::vector<uint8_t> rbuf;
403*f730973bSKevin Tung
404*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
405*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
406*f730973bSKevin Tung {
407*f730973bSKevin Tung error("Failed to set page 0 for get user data");
408*f730973bSKevin Tung co_return false;
409*f730973bSKevin Tung }
410*f730973bSKevin Tung
411*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::userData08);
412*f730973bSKevin Tung rbuf.resize(crcUserMultiDataLength + statusByteLength);
413*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
414*f730973bSKevin Tung {
415*f730973bSKevin Tung error("Failed to get user data on page 0");
416*f730973bSKevin Tung co_return false;
417*f730973bSKevin Tung }
418*f730973bSKevin Tung
419*f730973bSKevin Tung auto crcBytes = std::span(rbuf).subspan(statusByteLength);
420*f730973bSKevin Tung *checksum = bytesToInt<uint32_t>(crcBytes);
421*f730973bSKevin Tung
422*f730973bSKevin Tung debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
423*f730973bSKevin Tung co_return true;
424*f730973bSKevin Tung }
425*f730973bSKevin Tung
restoreDataFromNVM()426*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::restoreDataFromNVM()
427*f730973bSKevin Tung {
428*f730973bSKevin Tung static constexpr size_t nvmPmbusCtrlDataLength = 2;
429*f730973bSKevin Tung static constexpr uint16_t enableRestoreDataFromMTPMask = 0x0008;
430*f730973bSKevin Tung
431*f730973bSKevin Tung std::vector<uint8_t> tbuf;
432*f730973bSKevin Tung std::vector<uint8_t> rbuf;
433*f730973bSKevin Tung
434*f730973bSKevin Tung // enable restore data from MTP
435*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
436*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
437*f730973bSKevin Tung {
438*f730973bSKevin Tung error("Failed to set page 2 to enable restore data from MTP");
439*f730973bSKevin Tung co_return false;
440*f730973bSKevin Tung }
441*f730973bSKevin Tung
442*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl);
443*f730973bSKevin Tung rbuf.resize(nvmPmbusCtrlDataLength);
444*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
445*f730973bSKevin Tung {
446*f730973bSKevin Tung error("Failed to read NVM PMBUS Ctrl register");
447*f730973bSKevin Tung co_return false;
448*f730973bSKevin Tung }
449*f730973bSKevin Tung
450*f730973bSKevin Tung uint16_t data = ((rbuf[1] << 8) | rbuf[0]) | enableRestoreDataFromMTPMask;
451*f730973bSKevin Tung tbuf = buildByteVector(MPX9XXCmd::mfrNVMPmbusCtrl, data);
452*f730973bSKevin Tung rbuf.clear();
453*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
454*f730973bSKevin Tung {
455*f730973bSKevin Tung error("Failed to enable restore data from MTP");
456*f730973bSKevin Tung co_return false;
457*f730973bSKevin Tung }
458*f730973bSKevin Tung
459*f730973bSKevin Tung // restore data from NVM
460*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
461*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
462*f730973bSKevin Tung {
463*f730973bSKevin Tung error("Failed to set page 0 for restore MTP and verify");
464*f730973bSKevin Tung }
465*f730973bSKevin Tung
466*f730973bSKevin Tung tbuf = buildByteVector(PMBusCmd::restoreUserAll);
467*f730973bSKevin Tung if (!i2cInterface.sendReceive(tbuf, rbuf))
468*f730973bSKevin Tung {
469*f730973bSKevin Tung error("Failed to restore data from NVM");
470*f730973bSKevin Tung co_return false;
471*f730973bSKevin Tung }
472*f730973bSKevin Tung
473*f730973bSKevin Tung // wait restore data
474*f730973bSKevin Tung co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
475*f730973bSKevin Tung
476*f730973bSKevin Tung debug("Restored data from NVM success");
477*f730973bSKevin Tung
478*f730973bSKevin Tung co_return true;
479*f730973bSKevin Tung }
480*f730973bSKevin Tung
checkMTPCRC()481*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::checkMTPCRC()
482*f730973bSKevin Tung {
483*f730973bSKevin Tung uint32_t crc = 0;
484*f730973bSKevin Tung // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
485*f730973bSKevin Tung if (!co_await getCRC(&crc))
486*f730973bSKevin Tung // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
487*f730973bSKevin Tung {
488*f730973bSKevin Tung error("Failed to get CRC for MTP check");
489*f730973bSKevin Tung co_return false;
490*f730973bSKevin Tung }
491*f730973bSKevin Tung
492*f730973bSKevin Tung debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
493*f730973bSKevin Tung lg2::hex, configuration->crcMulti);
494*f730973bSKevin Tung
495*f730973bSKevin Tung co_return configuration->crcMulti == crc;
496*f730973bSKevin Tung }
497*f730973bSKevin Tung
forcedUpdateAllowed()498*f730973bSKevin Tung bool MPX9XX::forcedUpdateAllowed()
499*f730973bSKevin Tung {
500*f730973bSKevin Tung return true;
501*f730973bSKevin Tung }
502*f730973bSKevin Tung
updateFirmware(bool force)503*f730973bSKevin Tung sdbusplus::async::task<bool> MPX9XX::updateFirmware(bool force)
504*f730973bSKevin Tung {
505*f730973bSKevin Tung (void)force;
506*f730973bSKevin Tung
507*f730973bSKevin Tung if (!configuration)
508*f730973bSKevin Tung {
509*f730973bSKevin Tung error("Configuration not loaded");
510*f730973bSKevin Tung co_return false;
511*f730973bSKevin Tung }
512*f730973bSKevin Tung
513*f730973bSKevin Tung if (!co_await checkId(MPX9XXCmd::vendorId, configuration->vendorId))
514*f730973bSKevin Tung {
515*f730973bSKevin Tung co_return false;
516*f730973bSKevin Tung }
517*f730973bSKevin Tung
518*f730973bSKevin Tung if (!co_await checkId(MPX9XXCmd::deviceId, configuration->productId))
519*f730973bSKevin Tung {
520*f730973bSKevin Tung co_return false;
521*f730973bSKevin Tung }
522*f730973bSKevin Tung
523*f730973bSKevin Tung if (!co_await checkId(getConfigIdCmd(), configuration->configId))
524*f730973bSKevin Tung {
525*f730973bSKevin Tung co_return false;
526*f730973bSKevin Tung }
527*f730973bSKevin Tung
528*f730973bSKevin Tung if (!co_await unlockWriteProtect())
529*f730973bSKevin Tung {
530*f730973bSKevin Tung co_return false;
531*f730973bSKevin Tung }
532*f730973bSKevin Tung
533*f730973bSKevin Tung if (!co_await disableStoreFaultTriggering())
534*f730973bSKevin Tung {
535*f730973bSKevin Tung co_return false;
536*f730973bSKevin Tung }
537*f730973bSKevin Tung
538*f730973bSKevin Tung if (!co_await programAllRegisters())
539*f730973bSKevin Tung {
540*f730973bSKevin Tung co_return false;
541*f730973bSKevin Tung }
542*f730973bSKevin Tung
543*f730973bSKevin Tung if (!co_await restoreDataFromNVM())
544*f730973bSKevin Tung {
545*f730973bSKevin Tung co_return false;
546*f730973bSKevin Tung }
547*f730973bSKevin Tung
548*f730973bSKevin Tung if (!co_await checkMTPCRC())
549*f730973bSKevin Tung {
550*f730973bSKevin Tung co_return false;
551*f730973bSKevin Tung }
552*f730973bSKevin Tung
553*f730973bSKevin Tung co_return true;
554*f730973bSKevin Tung }
555*f730973bSKevin Tung
556*f730973bSKevin Tung } // namespace phosphor::software::VR
557