blob: 0572d00a1c25d9532d44adf8ff3c958680114422 [file] [log] [blame]
Leo Yangc1b36622025-10-28 10:39:55 +08001#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
13PHOSPHOR_LOG2_USING;
14
15namespace phosphor::software::VR
16{
17
18static constexpr size_t progNVMDelay = 300;
19static constexpr uint8_t NVMDoneMask = 0x80;
20static constexpr uint8_t NVMErrorMask = 0x40;
21static constexpr uint8_t pageZero = 0;
22
23enum 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
40const 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
55TDA38640A::TDA38640A(sdbusplus::async::context& ctx, uint16_t bus,
56 uint16_t address) :
57 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
58{}
59
60sdbusplus::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
86sdbusplus::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
100sdbusplus::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
124sdbusplus::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
161bool 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
235sdbusplus::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
250sdbusplus::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
279sdbusplus::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
297sdbusplus::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
387sdbusplus::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
446bool TDA38640A::forcedUpdateAllowed()
447{
448 return true;
449}
450
451sdbusplus::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