xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mpx9xx.cpp (revision f730973b554c83b7a8e85232e9c730a89d5f3692)
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