xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/tda38640a/tda38640a.cpp (revision c1b36628298d7799677680e70d455f85acf83650)
1 #include "tda38640a.hpp"
2 
3 #include "common/include/i2c/i2c.hpp"
4 #include "common/include/utils.hpp"
5 
6 #include <phosphor-logging/lg2.hpp>
7 
8 #include <iostream>
9 #include <sstream>
10 #include <string>
11 #include <vector>
12 
13 PHOSPHOR_LOG2_USING;
14 
15 namespace phosphor::software::VR
16 {
17 
18 static constexpr size_t progNVMDelay = 300;
19 static constexpr uint8_t NVMDoneMask = 0x80;
20 static constexpr uint8_t NVMErrorMask = 0x40;
21 static constexpr uint8_t pageZero = 0;
22 
23 enum class TDA38640ACmd : uint8_t
24 {
25     crcLowReg = 0xB0,
26     crcHighReg = 0xAE,
27     userWrRemain = 0xB8,
28     unlockRegsReg = 0xD4,
29     unlockRegsVal = 0x03, // Unlock i2c and PMBus address registers.
30     progCmdLowReg = 0xD6,
31     progCmdHighReg = 0xD7,
32     progCmdLowVal = 0x42, // 0x3f42 From datasheet, This will store the user
33                           // register in the next available nvm user image.
34     progCmdHighVal = 0x3F,
35     revisionReg = 0xFD,   // The silicon version value is stored in register
36                           // 0x00FD [7:0] of page 0.
37     pageReg = 0xff
38 };
39 
40 const std::unordered_set<uint16_t> user_section_otp_register{
41     0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
42     0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051,
43     0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A,
44     0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063,
45     0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C,
46     0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075,
47     0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x0202, 0x0204, 0x0220,
48     0x0240, 0x0242, 0x0243, 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D,
49     0x024E, 0x024F, 0x0250, 0x0251, 0x0252, 0x0256, 0x0257, 0x0266, 0x0267,
50     0x026A, 0x026C, 0x0270, 0x0272, 0x0273, 0x0280, 0x0281, 0x0282, 0x0288,
51     0x0289, 0x028A, 0x028C, 0x028D, 0x028E, 0x029E, 0x02A0, 0x02A2, 0x02AA,
52     0x02AB, 0x02AC, 0x02BC, 0x02BD, 0x02BE, 0x02BF, 0x02C0, 0x02C2, 0x02C8,
53     0x02CA, 0x0384, 0x0385};
54 
TDA38640A(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address)55 TDA38640A::TDA38640A(sdbusplus::async::context& ctx, uint16_t bus,
56                      uint16_t address) :
57     VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
58 {}
59 
getUserRemainingWrites(uint8_t * remain)60 sdbusplus::async::task<bool> TDA38640A::getUserRemainingWrites(uint8_t* remain)
61 {
62     std::vector<uint8_t> tbuf;
63     std::vector<uint8_t> rbuf;
64     uint16_t remainBits = 0;
65 
66     if (!(co_await setPage(pageZero)))
67     {
68         error("getUserRemainingWrites failed at setPage");
69         co_return false;
70     }
71 
72     tbuf = buildByteVector(TDA38640ACmd::userWrRemain);
73     rbuf.resize(2);
74     if (!i2cInterface.sendReceive(tbuf, rbuf))
75     {
76         error("getUserRemainingWrites failed with sendreceive");
77         co_return false;
78     }
79     remainBits = rbuf[0] | (rbuf[1] << 8);
80 
81     *remain = (16 - std::popcount(remainBits));
82 
83     co_return true;
84 }
85 
setPage(uint8_t page)86 sdbusplus::async::task<bool> TDA38640A::setPage(uint8_t page)
87 {
88     std::vector<uint8_t> tbuf;
89     std::vector<uint8_t> rbuf;
90 
91     tbuf = buildByteVector(TDA38640ACmd::pageReg, page);
92     if (!i2cInterface.sendReceive(tbuf, rbuf))
93     {
94         error("setPage failed with sendreceive");
95         co_return false;
96     }
97     co_return true;
98 }
99 
getDeviceRevision(uint8_t * revision)100 sdbusplus::async::task<bool> TDA38640A::getDeviceRevision(uint8_t* revision)
101 {
102     std::vector<uint8_t> tbuf;
103     std::vector<uint8_t> rbuf;
104 
105     if (!(co_await setPage(pageZero)))
106     {
107         error("getDeviceRevision failed at setPage");
108         co_return false;
109     }
110 
111     tbuf = buildByteVector(TDA38640ACmd::revisionReg);
112     rbuf.resize(1);
113     if (!i2cInterface.sendReceive(tbuf, rbuf))
114     {
115         error("getDeviceRevision failed with sendreceive");
116         co_return false;
117     }
118 
119     *revision = rbuf[0];
120 
121     co_return true;
122 }
123 
getCRC(uint32_t * sum)124 sdbusplus::async::task<bool> TDA38640A::getCRC(uint32_t* sum)
125 {
126     std::vector<uint8_t> tbuf;
127     std::vector<uint8_t> rbuf;
128 
129     uint32_t checksum = 0;
130 
131     if (!(co_await setPage(pageZero)))
132     {
133         error("getCRC failed at setPage");
134         co_return false;
135     }
136 
137     tbuf = buildByteVector(TDA38640ACmd::crcLowReg);
138     rbuf.resize(2);
139     if (!i2cInterface.sendReceive(tbuf, rbuf))
140     {
141         error("getCRC failed with sendreceive");
142         co_return false;
143     }
144 
145     checksum = rbuf[0] | (rbuf[1] << 8);
146 
147     tbuf = buildByteVector(TDA38640ACmd::crcHighReg);
148     if (!i2cInterface.sendReceive(tbuf, rbuf))
149     {
150         error("getCRC failed with sendreceive");
151         co_return false;
152     }
153 
154     checksum |= (rbuf[0] << 16) | (rbuf[1] << 24);
155 
156     *sum = checksum;
157 
158     co_return true;
159 }
160 
parseImage(const uint8_t * image,size_t imageSize)161 bool TDA38640A::parseImage(const uint8_t* image, size_t imageSize)
162 {
163     std::string content(reinterpret_cast<const char*>(image), imageSize);
164     std::istringstream imageStream(content);
165     std::string line;
166 
167     configuration.clear();
168 
169     bool inConfigData = false;
170     while (std::getline(imageStream, line))
171     {
172         if (line.find("Part Number :") != std::string::npos)
173         {
174             if (line.back() == '\r')
175             {
176                 line.pop_back();
177             }
178             std::string s(1, line.back());
179             configuration.rev =
180                 static_cast<uint8_t>(std::stoul(s, nullptr, 16));
181         }
182 
183         if (line.find("Configuration Checksum :") != std::string::npos)
184         {
185             size_t pos = line.find("0x");
186             if (pos != std::string::npos)
187             {
188                 std::string hexStr = line.substr(pos + 2);
189                 configuration.checksum = std::stoul(hexStr, nullptr, 16);
190             }
191         }
192 
193         if (line.find("[Configuration Data]") != std::string::npos)
194         {
195             inConfigData = true;
196             continue;
197         }
198         if (line.find("[End Configuration Data]") != std::string::npos)
199         {
200             break;
201         }
202         if (inConfigData && !line.empty())
203         {
204             std::istringstream lineStream(line);
205             std::string seg;
206             std::vector<uint8_t> dataVector;
207             while (lineStream >> seg)
208             {
209                 if (seg.length() == 2)
210                 {
211                     uint8_t data =
212                         static_cast<uint8_t>(std::stoi(seg, nullptr, 16));
213                     dataVector.push_back(data);
214                 }
215                 else
216                 {
217                     uint16_t offset =
218                         static_cast<uint16_t>(std::stoi(seg, nullptr, 16));
219                     configuration.offsets.push_back(offset);
220                 }
221             }
222             configuration.data.push_back(dataVector);
223         }
224     }
225 
226     if (configuration.offsets.size() != configuration.data.size())
227     {
228         error("parseImage failed. Data line mismatch.");
229         return false;
230     }
231 
232     return true;
233 }
234 
unlockDevice()235 sdbusplus::async::task<bool> TDA38640A::unlockDevice()
236 {
237     std::vector<uint8_t> tbuf;
238     std::vector<uint8_t> rbuf;
239 
240     tbuf = buildByteVector(TDA38640ACmd::unlockRegsReg,
241                            TDA38640ACmd::unlockRegsVal);
242     if (!i2cInterface.sendReceive(tbuf, rbuf))
243     {
244         error("unlockDevice failed with sendreceive");
245         co_return false;
246     }
247     co_return true;
248 }
249 
programmingCmd()250 sdbusplus::async::task<bool> TDA38640A::programmingCmd()
251 {
252     std::vector<uint8_t> tbuf;
253     std::vector<uint8_t> rbuf;
254 
255     if (!(co_await setPage(pageZero)))
256     {
257         error("programmingCmd failed at setPage 0.");
258         co_return false;
259     }
260 
261     tbuf = buildByteVector(TDA38640ACmd::progCmdHighReg,
262                            TDA38640ACmd::progCmdHighVal);
263     if (!i2cInterface.sendReceive(tbuf, rbuf))
264     {
265         error("programmingCmd high bit failed with sendreceive.");
266         co_return false;
267     }
268 
269     tbuf = buildByteVector(TDA38640ACmd::progCmdLowReg,
270                            TDA38640ACmd::progCmdLowVal);
271     if (!i2cInterface.sendReceive(tbuf, rbuf))
272     {
273         error("programmingCmd low bit failed with sendreceive.");
274         co_return false;
275     }
276     co_return true;
277 }
278 
getProgStatus(uint8_t * status)279 sdbusplus::async::task<bool> TDA38640A::getProgStatus(uint8_t* status)
280 {
281     std::vector<uint8_t> tbuf;
282     std::vector<uint8_t> rbuf;
283 
284     tbuf = buildByteVector(TDA38640ACmd::progCmdHighReg);
285     rbuf.resize(1);
286     if (!i2cInterface.sendReceive(tbuf, rbuf))
287     {
288         error("getProgStatus failed with sendreceive");
289         co_return false;
290     }
291 
292     *status = rbuf[0];
293 
294     co_return true;
295 }
296 
program()297 sdbusplus::async::task<bool> TDA38640A::program()
298 {
299     std::vector<uint8_t> tbuf;
300     std::vector<uint8_t> rbuf;
301     uint8_t status;
302     uint8_t retry = 3;
303 
304     if (!(co_await unlockDevice()))
305     {
306         error("program failed at unlockDevice");
307         co_return false;
308     }
309 
310     uint8_t page = 0;
311     uint8_t address = 0;
312     for (size_t i = 0; i < configuration.offsets.size(); i++)
313     {
314         page = configuration.offsets[i] >> 8;
315         if (!(co_await setPage(page)))
316         {
317             error("program failed at setPage");
318             co_return false;
319         }
320 
321         for (uint8_t bias = 0; bias < 16; bias++)
322         {
323             uint16_t full_addr = configuration.offsets[i] + bias;
324 
325             if (user_section_otp_register.find(full_addr) ==
326                 user_section_otp_register.end())
327             {
328                 debug(
329                     "program at address {ADDR} not belone to user_section_otp_register.",
330                     "ADDR", lg2::hex, full_addr);
331                 continue;
332             }
333 
334             address = (configuration.offsets[i] & 0xFF) + bias;
335 
336             tbuf = buildByteVector(address, configuration.data[i][bias]);
337             if (!i2cInterface.sendReceive(tbuf, rbuf))
338             {
339                 error("program failed with sendreceive");
340                 co_return false;
341             }
342             debug("programming : at {PAGE} {ADDR} with {DATA}", "PAGE",
343                   lg2::hex, page, "ADDR", lg2::hex, address, "DATA", lg2::hex,
344                   configuration.data[i][bias]);
345         }
346     }
347 
348     if (!(co_await programmingCmd()))
349     {
350         error("program failed at programmingCmd");
351         co_return false;
352     }
353 
354     for (uint8_t r = 0; r < retry; r++)
355     {
356         co_await sdbusplus::async::sleep_for(
357             ctx, std::chrono::milliseconds(progNVMDelay));
358 
359         if (!(co_await getProgStatus(&status)))
360         {
361             error("program failed at getProgStatus");
362             co_return false;
363         }
364 
365         if ((status & NVMDoneMask) == 0 || (status & NVMErrorMask) != 0)
366         {
367             if ((status & NVMDoneMask) == 0)
368             {
369                 error(
370                     "getProgStatus failed with 0x00D7[7] == 0, Programming command not completed. retry...");
371             }
372             if ((status & NVMErrorMask) != 0)
373             {
374                 error(
375                     "getProgStatus failed with 0x00D7[6] == 1, The previous NVM operation encountered an error. retry...");
376             }
377         }
378         else
379         {
380             debug("ProgStatus ok.");
381             co_return true;
382         }
383     }
384     co_return false;
385 }
386 
verifyImage(const uint8_t * image,size_t imageSize)387 sdbusplus::async::task<bool> TDA38640A::verifyImage(const uint8_t* image,
388                                                     size_t imageSize)
389 {
390     uint8_t remain = 0;
391     uint8_t devRev = 0;
392     uint32_t devCrc = 0;
393 
394     if (!parseImage(image, imageSize))
395     {
396         error("verifyImage failed at parseImage");
397         co_return false;
398     }
399 
400     if (!(co_await getUserRemainingWrites(&remain)))
401     {
402         error("program failed at getUserRemainingWrites");
403         co_return false;
404     }
405     debug("User Remaining Writes from device: {REMAIN}", "REMAIN", lg2::dec,
406           remain);
407 
408     if (!remain)
409     {
410         error("program failed with no user remaining writes left on device");
411         co_return false;
412     }
413 
414     if (!(co_await getDeviceRevision(&devRev)))
415     {
416         error("program failed at getDeviceRevision");
417         co_return false;
418     }
419     debug("Device revision read from device: {REV}", "REV", lg2::hex, devRev);
420 
421     if (devRev != configuration.rev)
422     {
423         error(
424             "program failed with revision of device and configuration are not equal");
425         co_return false;
426     }
427 
428     if (!(co_await getCRC(&devCrc)))
429     {
430         error("program failed at getCRC");
431         co_return false;
432     }
433 
434     debug("CRC from device: {CRC}", "CRC", lg2::hex, devCrc);
435     debug("CRC from config: {CRC}", "CRC", lg2::hex, configuration.checksum);
436 
437     if (devCrc == configuration.checksum)
438     {
439         error("program failed with same CRC value at device and configuration");
440         co_return false;
441     }
442 
443     co_return true;
444 }
445 
forcedUpdateAllowed()446 bool TDA38640A::forcedUpdateAllowed()
447 {
448     return true;
449 }
450 
updateFirmware(bool force)451 sdbusplus::async::task<bool> TDA38640A::updateFirmware(bool force)
452 {
453     (void)force;
454     if (!(co_await program()))
455     {
456         error("programing TDA38640A failed");
457         co_return false;
458     }
459 
460     co_return true;
461 }
462 
463 } // namespace phosphor::software::VR
464