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