17e446a40SChristopher Meis #include "xdpe1x2xx.hpp"
27e446a40SChristopher Meis
37e446a40SChristopher Meis #include "common/include/i2c/i2c.hpp"
47e446a40SChristopher Meis
57e446a40SChristopher Meis #include <unistd.h>
67e446a40SChristopher Meis
77e446a40SChristopher Meis #include <phosphor-logging/lg2.hpp>
87e446a40SChristopher Meis
97e446a40SChristopher Meis #include <cstdio>
107e446a40SChristopher Meis
117e446a40SChristopher Meis #define REMAINING_TIMES(x, y) (((((x)[1]) << 8) | ((x)[0])) / (y))
127e446a40SChristopher Meis
137e446a40SChristopher Meis PHOSPHOR_LOG2_USING;
147e446a40SChristopher Meis
157e446a40SChristopher Meis namespace phosphor::software::VR
167e446a40SChristopher Meis {
177e446a40SChristopher Meis
187e446a40SChristopher Meis enum RevisionCode
197e446a40SChristopher Meis {
207e446a40SChristopher Meis REV_A = 0x00,
217e446a40SChristopher Meis REV_B,
227e446a40SChristopher Meis REV_C,
237e446a40SChristopher Meis REV_D,
247e446a40SChristopher Meis };
257e446a40SChristopher Meis
267e446a40SChristopher Meis enum ProductID
277e446a40SChristopher Meis {
28*fd34144bSChristopher Meis ProductIDXDPE15254 = 0x90, // Revision C,D
29*fd34144bSChristopher Meis ProductIDXDPE15284 = 0x8A, // Revision A,B,C,D
30*fd34144bSChristopher Meis ProductIDXDPE19283AC = 0x95, // Revision A,B,C
31*fd34144bSChristopher Meis ProductIDXDPE19283D = 0xAE, // Revision D
32*fd34144bSChristopher Meis ProductIDXDPE192C3AC = 0x96, // Revision A,B,C
33*fd34144bSChristopher Meis ProductIDXDPE192C3D = 0xAF, // Revision D
347e446a40SChristopher Meis };
357e446a40SChristopher Meis
36*fd34144bSChristopher Meis constexpr uint8_t PMBusICDeviceID = 0xAD;
37*fd34144bSChristopher Meis constexpr uint8_t PMBusSTLCml = 0x7E;
38*fd34144bSChristopher Meis constexpr uint8_t IFXICDeviceIDLen = 2;
39*fd34144bSChristopher Meis constexpr uint8_t IFXMFRAHBAddr = 0xCE;
40*fd34144bSChristopher Meis constexpr uint8_t IFXMFRRegWrite = 0xDE;
41*fd34144bSChristopher Meis constexpr uint8_t IFXMFRFwCmdData = 0xFD;
42*fd34144bSChristopher Meis constexpr uint8_t IFXMFRFwCmd = 0xFE;
43*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdReset = 0x0e;
44*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdRmng = 0x10;
45*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdGetHWAddress = 0x2E;
46*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdOTPConfSTO = 0x11;
47*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdOTPFileInvd = 0x12;
48*fd34144bSChristopher Meis constexpr uint8_t MFRFwCmdGetCRC = 0x2D;
49*fd34144bSChristopher Meis constexpr int XDPE15284CConfSize = 1344;
50*fd34144bSChristopher Meis constexpr int XDPE19283BConfSize = 1416;
51*fd34144bSChristopher Meis constexpr uint8_t VRWarnRemaining = 3;
52*fd34144bSChristopher Meis constexpr uint8_t SectTrim = 0x02;
53*fd34144bSChristopher Meis
54*fd34144bSChristopher Meis constexpr uint16_t MFRDefaultWaitTime = 20;
55*fd34144bSChristopher Meis constexpr uint16_t MFRGetHWAddressWaitTime = 5;
56*fd34144bSChristopher Meis constexpr uint16_t MFROTPFileInvalidationWaitTime = 100;
57*fd34144bSChristopher Meis constexpr uint16_t MFRSectionInvalidationWaitTime = 4;
58*fd34144bSChristopher Meis constexpr uint16_t VRResetDelay = 500;
59*fd34144bSChristopher Meis
60*fd34144bSChristopher Meis constexpr uint32_t CRC32Poly = 0xEDB88320;
617e446a40SChristopher Meis
627e446a40SChristopher Meis const char* const AddressField = "PMBus Address :";
637e446a40SChristopher Meis const char* const ChecksumField = "Checksum :";
64*fd34144bSChristopher Meis const char* const DataStartTag = "[Configuration Data]";
657e446a40SChristopher Meis const char* const DataEndTag = "[End Configuration Data]";
667e446a40SChristopher Meis const char* const DataComment = "//";
677e446a40SChristopher Meis const char* const DataXV = "XV";
687e446a40SChristopher Meis
XDPE1X2XX(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address)697e446a40SChristopher Meis XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
707e446a40SChristopher Meis uint16_t address) :
717e446a40SChristopher Meis VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
727e446a40SChristopher Meis {}
737e446a40SChristopher Meis
getDeviceId(uint8_t * deviceID)747e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
757e446a40SChristopher Meis {
767e446a40SChristopher Meis uint8_t tbuf[16] = {0};
777e446a40SChristopher Meis tbuf[0] = PMBusICDeviceID;
787e446a40SChristopher Meis tbuf[1] = 2;
797e446a40SChristopher Meis uint8_t tSize = 1;
807e446a40SChristopher Meis uint8_t rbuf[16] = {0};
817e446a40SChristopher Meis uint8_t rSize = IFXICDeviceIDLen + 1;
827e446a40SChristopher Meis
83*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize)))
847e446a40SChristopher Meis {
857e446a40SChristopher Meis error("Failed to get device ID");
867e446a40SChristopher Meis co_return false;
877e446a40SChristopher Meis }
887e446a40SChristopher Meis
89*fd34144bSChristopher Meis // According to datasheet:
90*fd34144bSChristopher Meis // rbuf[1]: device revision
91*fd34144bSChristopher Meis // rbuf[2]: device id
927e446a40SChristopher Meis std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
93*fd34144bSChristopher Meis info.deviceRev = deviceID[0];
94*fd34144bSChristopher Meis info.deviceId = deviceID[1];
95*fd34144bSChristopher Meis debug("VR Device ID: {ID}", "ID", lg2::hex, rbuf[2]);
96*fd34144bSChristopher Meis debug("VR Device Rev: {REV}", "REV", lg2::hex, rbuf[1]);
977e446a40SChristopher Meis
987e446a40SChristopher Meis co_return true;
997e446a40SChristopher Meis }
1007e446a40SChristopher Meis
mfrFWcmd(uint8_t cmd,uint16_t processTime,uint8_t * data,uint8_t * resp)101*fd34144bSChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(
102*fd34144bSChristopher Meis uint8_t cmd, uint16_t processTime, uint8_t* data, uint8_t* resp)
1037e446a40SChristopher Meis {
1047e446a40SChristopher Meis uint8_t tBuf[16] = {0};
1057e446a40SChristopher Meis uint8_t rBuf[16] = {0};
1067e446a40SChristopher Meis uint8_t tSize = 0;
1077e446a40SChristopher Meis uint8_t rSize = 0;
1087e446a40SChristopher Meis
1097e446a40SChristopher Meis if (data)
1107e446a40SChristopher Meis {
1117e446a40SChristopher Meis tBuf[0] = IFXMFRFwCmdData;
1127e446a40SChristopher Meis tBuf[1] = 4; // Block write 4 bytes
1137e446a40SChristopher Meis tSize = 6;
1147e446a40SChristopher Meis std::memcpy(&tBuf[2], data, 4);
115*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
116*fd34144bSChristopher Meis rSize)))
1177e446a40SChristopher Meis {
1187e446a40SChristopher Meis error("Failed to send MFR command: {CMD}", "CMD",
1197e446a40SChristopher Meis std::string("IFXMFRFwCmdDAta"));
1207e446a40SChristopher Meis co_return false;
1217e446a40SChristopher Meis }
1227e446a40SChristopher Meis }
1237e446a40SChristopher Meis
1247e446a40SChristopher Meis co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
1257e446a40SChristopher Meis
1267e446a40SChristopher Meis tBuf[0] = IFXMFRFwCmd;
1277e446a40SChristopher Meis tBuf[1] = cmd;
1287e446a40SChristopher Meis tSize = 2;
1297e446a40SChristopher Meis rSize = 0;
130*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize)))
1317e446a40SChristopher Meis {
1327e446a40SChristopher Meis error("Failed to send MFR command: {CMD}", "CMD",
1337e446a40SChristopher Meis std::string("IFXMFRFwCmd"));
1347e446a40SChristopher Meis co_return false;
1357e446a40SChristopher Meis }
1367e446a40SChristopher Meis
137*fd34144bSChristopher Meis co_await sdbusplus::async::sleep_for(
138*fd34144bSChristopher Meis ctx, std::chrono::milliseconds(processTime));
1397e446a40SChristopher Meis
1407e446a40SChristopher Meis if (resp)
1417e446a40SChristopher Meis {
1427e446a40SChristopher Meis tBuf[0] = IFXMFRFwCmdData;
1437e446a40SChristopher Meis tSize = 1;
1447e446a40SChristopher Meis rSize = 6;
145*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
146*fd34144bSChristopher Meis rSize)))
1477e446a40SChristopher Meis {
1487e446a40SChristopher Meis error("Failed to send MFR command: {CMD}", "CMD",
1497e446a40SChristopher Meis std::string("IFXMFRFwCmdData"));
1507e446a40SChristopher Meis co_return false;
1517e446a40SChristopher Meis }
1527e446a40SChristopher Meis if (rBuf[0] != 4)
1537e446a40SChristopher Meis {
1547e446a40SChristopher Meis error(
1557e446a40SChristopher Meis "Failed to receive MFR response with unexpected response size");
1567e446a40SChristopher Meis co_return false;
1577e446a40SChristopher Meis }
1587e446a40SChristopher Meis std::memcpy(resp, rBuf + 1, 4);
1597e446a40SChristopher Meis }
1607e446a40SChristopher Meis
1617e446a40SChristopher Meis co_return true;
1627e446a40SChristopher Meis }
1637e446a40SChristopher Meis
getRemainingWrites(uint8_t * remain)1647e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
1657e446a40SChristopher Meis {
166*fd34144bSChristopher Meis // According to datasheet:
167*fd34144bSChristopher Meis // remaingin OTP size = rBuf[0] + 256 * rBuf[1]
1687e446a40SChristopher Meis uint8_t tBuf[16] = {0};
1697e446a40SChristopher Meis uint8_t rBuf[16] = {0};
1707e446a40SChristopher Meis uint8_t devId[2] = {0};
1717e446a40SChristopher Meis
172*fd34144bSChristopher Meis if (!(co_await this->mfrFWcmd(MFRFwCmdRmng, MFRDefaultWaitTime, tBuf,
173*fd34144bSChristopher Meis rBuf)))
1747e446a40SChristopher Meis {
1757e446a40SChristopher Meis error("Failed to request remaining writes");
1767e446a40SChristopher Meis co_return false;
1777e446a40SChristopher Meis }
1787e446a40SChristopher Meis
179*fd34144bSChristopher Meis if (!(co_await this->getDeviceId(devId)))
1807e446a40SChristopher Meis {
1817e446a40SChristopher Meis error("Failed to request device ID for remaining writes");
1827e446a40SChristopher Meis co_return false;
1837e446a40SChristopher Meis }
1847e446a40SChristopher Meis
1857e446a40SChristopher Meis int configSize = getConfigSize(devId[1], devId[0]);
1867e446a40SChristopher Meis if (configSize < 0)
1877e446a40SChristopher Meis {
1887e446a40SChristopher Meis error("Failed to request valid configuration size");
1897e446a40SChristopher Meis co_return false;
1907e446a40SChristopher Meis }
1917e446a40SChristopher Meis
1927e446a40SChristopher Meis *remain = REMAINING_TIMES(rBuf, configSize);
1937e446a40SChristopher Meis
194*fd34144bSChristopher Meis co_return true;
1957e446a40SChristopher Meis }
1967e446a40SChristopher Meis
getConfigSize(uint8_t deviceId,uint8_t revision)1977e446a40SChristopher Meis int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
1987e446a40SChristopher Meis {
1997e446a40SChristopher Meis int size = -1;
2007e446a40SChristopher Meis
2017e446a40SChristopher Meis switch (deviceId)
2027e446a40SChristopher Meis {
203*fd34144bSChristopher Meis case ProductIDXDPE19283AC:
2047e446a40SChristopher Meis if (revision == REV_B)
2057e446a40SChristopher Meis {
2067e446a40SChristopher Meis size = XDPE19283BConfSize;
2077e446a40SChristopher Meis }
2087e446a40SChristopher Meis break;
2097e446a40SChristopher Meis case ProductIDXDPE15284:
2107e446a40SChristopher Meis size = XDPE15284CConfSize;
2117e446a40SChristopher Meis break;
2127e446a40SChristopher Meis default:
2137e446a40SChristopher Meis error(
2147e446a40SChristopher Meis "Failed to get configuration size of {DEVID} with revision {REV}",
2157e446a40SChristopher Meis "DEVID", deviceId, "REV", revision);
2167e446a40SChristopher Meis return -1;
2177e446a40SChristopher Meis }
2187e446a40SChristopher Meis
2197e446a40SChristopher Meis return size;
2207e446a40SChristopher Meis }
2217e446a40SChristopher Meis
getCRC(uint32_t * checksum)2227e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
2237e446a40SChristopher Meis {
2247e446a40SChristopher Meis uint8_t tBuf[16] = {0};
2257e446a40SChristopher Meis uint8_t rBuf[16] = {0};
2267e446a40SChristopher Meis
227*fd34144bSChristopher Meis if (!(co_await this->mfrFWcmd(MFRFwCmdGetCRC, MFRDefaultWaitTime, tBuf,
228*fd34144bSChristopher Meis rBuf)))
2297e446a40SChristopher Meis {
2307e446a40SChristopher Meis error("Failed to get CRC value");
2317e446a40SChristopher Meis co_return false;
2327e446a40SChristopher Meis }
2337e446a40SChristopher Meis
2347e446a40SChristopher Meis *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
2357e446a40SChristopher Meis (static_cast<uint32_t>(rBuf[2]) << 16) |
2367e446a40SChristopher Meis (static_cast<uint32_t>(rBuf[1]) << 8) |
2377e446a40SChristopher Meis (static_cast<uint32_t>(rBuf[0]));
2387e446a40SChristopher Meis
2397e446a40SChristopher Meis co_return true;
2407e446a40SChristopher Meis }
2417e446a40SChristopher Meis
program(bool force)2427e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
2437e446a40SChristopher Meis {
2447e446a40SChristopher Meis uint8_t tBuf[16] = {0};
2457e446a40SChristopher Meis uint8_t rBuf[16] = {0};
2467e446a40SChristopher Meis uint8_t remain = 0;
2477e446a40SChristopher Meis uint32_t sum = 0;
2487e446a40SChristopher Meis int size = 0;
2497e446a40SChristopher Meis
2507e446a40SChristopher Meis // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
251*fd34144bSChristopher Meis if (!(co_await getCRC(&sum)))
2527e446a40SChristopher Meis // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
2537e446a40SChristopher Meis {
2547e446a40SChristopher Meis error("Failed to program the VR");
2557e446a40SChristopher Meis co_return -1;
2567e446a40SChristopher Meis }
2577e446a40SChristopher Meis
258*fd34144bSChristopher Meis debug("CRC before programming: {CRC}", "CRC", lg2::hex, sum);
259*fd34144bSChristopher Meis debug("CRC of configuration: {CRC}", "CRC", lg2::hex, configuration.sumExp);
260*fd34144bSChristopher Meis
2617e446a40SChristopher Meis if (!force && (sum == configuration.sumExp))
2627e446a40SChristopher Meis {
2637e446a40SChristopher Meis error("Failed to program the VR - CRC value are equal with no force");
2647e446a40SChristopher Meis co_return -1;
2657e446a40SChristopher Meis }
2667e446a40SChristopher Meis
2677e446a40SChristopher Meis // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
268*fd34144bSChristopher Meis if (!(co_await this->getRemainingWrites(&remain)))
2697e446a40SChristopher Meis // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
2707e446a40SChristopher Meis {
2717e446a40SChristopher Meis error("Failed to program the VR - unable to obtain remaing writes");
2727e446a40SChristopher Meis co_return -1;
2737e446a40SChristopher Meis }
2747e446a40SChristopher Meis
275*fd34144bSChristopher Meis debug("Remaining write cycles of VR: {REMAIN}", "REMAIN", remain);
276*fd34144bSChristopher Meis
2777e446a40SChristopher Meis if (!remain)
2787e446a40SChristopher Meis {
2797e446a40SChristopher Meis error("Failed to program the VR - no remaining write cycles left");
2807e446a40SChristopher Meis co_return -1;
2817e446a40SChristopher Meis }
2827e446a40SChristopher Meis
2837e446a40SChristopher Meis if (!force && (remain <= VRWarnRemaining))
2847e446a40SChristopher Meis {
2857e446a40SChristopher Meis error(
2867e446a40SChristopher Meis "Failed to program the VR - {REMAIN} remaining writes left and not force",
2877e446a40SChristopher Meis "REMAIN", remain);
2887e446a40SChristopher Meis co_return -1;
2897e446a40SChristopher Meis }
2907e446a40SChristopher Meis
2917e446a40SChristopher Meis // Added reprogramming of the entire configuration file.
2927e446a40SChristopher Meis // Except for the trim section, all other data will be replaced.
2937e446a40SChristopher Meis // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
2947e446a40SChristopher Meis // and XVcode. If the old sections are not invalidated in OTP, they can
2957e446a40SChristopher Meis // affect the CRC calculation.
296*fd34144bSChristopher Meis debug("Invalidate current Configuration");
2977e446a40SChristopher Meis
2987e446a40SChristopher Meis tBuf[0] = 0xfe;
2997e446a40SChristopher Meis tBuf[1] = 0xfe;
3007e446a40SChristopher Meis tBuf[2] = 0x00;
3017e446a40SChristopher Meis tBuf[3] = 0x00;
3027e446a40SChristopher Meis
303*fd34144bSChristopher Meis if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
304*fd34144bSChristopher Meis MFROTPFileInvalidationWaitTime, tBuf, NULL)))
3057e446a40SChristopher Meis {
306*fd34144bSChristopher Meis error("Failed to program the VR - Invalidation of current FW");
307*fd34144bSChristopher Meis co_return false;
3087e446a40SChristopher Meis }
3097e446a40SChristopher Meis
3107e446a40SChristopher Meis for (int i = 0; i < configuration.sectCnt; i++)
3117e446a40SChristopher Meis {
312*fd34144bSChristopher Meis debug("Programming section: {SEC}", "SEC", i);
3137e446a40SChristopher Meis struct configSect* sect = &configuration.section[i];
3147e446a40SChristopher Meis if (sect == NULL)
3157e446a40SChristopher Meis {
3167e446a40SChristopher Meis error(
3177e446a40SChristopher Meis "Failed to program the VR - unexpected NULL section in config");
318*fd34144bSChristopher Meis co_return false;
3197e446a40SChristopher Meis }
3207e446a40SChristopher Meis
3217e446a40SChristopher Meis if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
3227e446a40SChristopher Meis {
323*fd34144bSChristopher Meis debug("Section Type: {TYPE}", "TYPE", lg2::hex,
324*fd34144bSChristopher Meis configuration.section[i].type);
325*fd34144bSChristopher Meis
326*fd34144bSChristopher Meis // clear bit0 of PMBUS_STS_CML
3277e446a40SChristopher Meis tBuf[0] = PMBusSTLCml;
3287e446a40SChristopher Meis tBuf[1] = 0x1;
3297e446a40SChristopher Meis uint8_t tSize = 2;
3307e446a40SChristopher Meis uint8_t rSize = 0;
331*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
332*fd34144bSChristopher Meis rSize)))
3337e446a40SChristopher Meis {
3347e446a40SChristopher Meis error("Failed to program the VR on sendReceive {CMD}", "CMD",
3357e446a40SChristopher Meis std::string("PMBusSTLCml"));
336*fd34144bSChristopher Meis co_return false;
3377e446a40SChristopher Meis }
3387e446a40SChristopher Meis
339*fd34144bSChristopher Meis debug("Invalidating section type: {TYPE}", "TYPE", sect->type);
3407e446a40SChristopher Meis tBuf[0] = sect->type;
3417e446a40SChristopher Meis tBuf[1] = 0x00;
3427e446a40SChristopher Meis tBuf[2] = 0x00;
3437e446a40SChristopher Meis tBuf[3] = 0x00;
3447e446a40SChristopher Meis
345*fd34144bSChristopher Meis if (!(co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd,
346*fd34144bSChristopher Meis MFRSectionInvalidationWaitTime, tBuf,
347*fd34144bSChristopher Meis NULL)))
3487e446a40SChristopher Meis {
3497e446a40SChristopher Meis error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
3507e446a40SChristopher Meis std::string("MFRFwCmdOTPFileInvd"));
351*fd34144bSChristopher Meis co_return false;
3527e446a40SChristopher Meis }
3537e446a40SChristopher Meis
354*fd34144bSChristopher Meis // set scratchpad addr
355*fd34144bSChristopher Meis // XDPE192XX Rev A/B: 0x2005e000
356*fd34144bSChristopher Meis // Rev C : 0x2005e400
357*fd34144bSChristopher Meis // Rev D : 0x2005f000
358*fd34144bSChristopher Meis
359*fd34144bSChristopher Meis debug("Setting scratchpad address: {ADDR}", "ADDR", lg2::hex,
360*fd34144bSChristopher Meis info.scratchPadAddress);
3617e446a40SChristopher Meis
3627e446a40SChristopher Meis tBuf[0] = IFXMFRAHBAddr;
3637e446a40SChristopher Meis tBuf[1] = 4;
364*fd34144bSChristopher Meis tBuf[2] = (info.scratchPadAddress) & 0xFF;
365*fd34144bSChristopher Meis tBuf[3] = (info.scratchPadAddress >> 8) & 0xFF;
366*fd34144bSChristopher Meis tBuf[4] = (info.scratchPadAddress >> 16) & 0xFF;
367*fd34144bSChristopher Meis tBuf[5] = (info.scratchPadAddress >> 24) & 0xFF;
3687e446a40SChristopher Meis tSize = 6;
3697e446a40SChristopher Meis rSize = 0;
3707e446a40SChristopher Meis
371*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
372*fd34144bSChristopher Meis rSize)))
3737e446a40SChristopher Meis {
3747e446a40SChristopher Meis error("Failed to program VR on sendReceive on {CMD}", "CMD",
3757e446a40SChristopher Meis std::string("IFXMFRAHBAddr"));
376*fd34144bSChristopher Meis co_return false;
3777e446a40SChristopher Meis }
3787e446a40SChristopher Meis
3797e446a40SChristopher Meis co_await sdbusplus::async::sleep_for(
3807e446a40SChristopher Meis ctx, std::chrono::microseconds(10000));
3817e446a40SChristopher Meis size = 0;
3827e446a40SChristopher Meis }
3837e446a40SChristopher Meis
3847e446a40SChristopher Meis // programm into scratchpad
3857e446a40SChristopher Meis for (int j = 0; j < sect->dataCnt; j++)
3867e446a40SChristopher Meis {
3877e446a40SChristopher Meis tBuf[0] = IFXMFRRegWrite;
3887e446a40SChristopher Meis tBuf[1] = 4;
3897e446a40SChristopher Meis uint8_t tSize = 6;
3907e446a40SChristopher Meis uint8_t rSize = 0;
3917e446a40SChristopher Meis memcpy(&tBuf[2], §->data[j], 4);
392*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
393*fd34144bSChristopher Meis rSize)))
3947e446a40SChristopher Meis {
3957e446a40SChristopher Meis error("Failed to program the VR on sendReceive {CMD}", "CMD",
3967e446a40SChristopher Meis std::string("IFXMFRRegWrite"));
397*fd34144bSChristopher Meis co_return false;
3987e446a40SChristopher Meis }
399*fd34144bSChristopher Meis co_await sdbusplus::async::sleep_for(ctx,
400*fd34144bSChristopher Meis std::chrono::milliseconds(10));
4017e446a40SChristopher Meis }
4027e446a40SChristopher Meis
4037e446a40SChristopher Meis size += sect->dataCnt * 4;
4047e446a40SChristopher Meis if ((i + 1 >= configuration.sectCnt) ||
4057e446a40SChristopher Meis (sect->type != configuration.section[i + 1].type))
4067e446a40SChristopher Meis {
407*fd34144bSChristopher Meis // wait for programming soak (2ms/byte, at least 200ms)
408*fd34144bSChristopher Meis // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
409*fd34144bSChristopher Meis uint16_t soakTime = 100 * ((size / 50) + 2);
410*fd34144bSChristopher Meis
4117e446a40SChristopher Meis // Upload to scratchpad
412*fd34144bSChristopher Meis debug("Upload from scratch pad to OTP with soak time: {TIME}ms",
413*fd34144bSChristopher Meis "TIME", soakTime);
4147e446a40SChristopher Meis std::memcpy(tBuf, &size, 2);
4157e446a40SChristopher Meis tBuf[2] = 0x00;
4167e446a40SChristopher Meis tBuf[3] = 0x00;
417*fd34144bSChristopher Meis if (!(co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, soakTime, tBuf,
418*fd34144bSChristopher Meis NULL)))
4197e446a40SChristopher Meis {
4207e446a40SChristopher Meis error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
4217e446a40SChristopher Meis std::string("MFRFwCmdOTPConfSTO"));
422*fd34144bSChristopher Meis co_return false;
4237e446a40SChristopher Meis }
4247e446a40SChristopher Meis
425*fd34144bSChristopher Meis // Read status faults after programming
4267e446a40SChristopher Meis tBuf[0] = PMBusSTLCml;
4277e446a40SChristopher Meis uint8_t tSize = 1;
4287e446a40SChristopher Meis uint8_t rSize = 1;
429*fd34144bSChristopher Meis if (!(co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
430*fd34144bSChristopher Meis rSize)))
4317e446a40SChristopher Meis {
4327e446a40SChristopher Meis error("Failed to program VR on sendReceive {CMD}", "CMD",
4337e446a40SChristopher Meis std::string("PMBusSTLCml"));
434*fd34144bSChristopher Meis co_return false;
4357e446a40SChristopher Meis }
4367e446a40SChristopher Meis if (rBuf[0] & 0x01)
4377e446a40SChristopher Meis {
438*fd34144bSChristopher Meis error("Failed to program VR - status fault indicated error");
4397e446a40SChristopher Meis co_return false;
4407e446a40SChristopher Meis }
441*fd34144bSChristopher Meis }
442*fd34144bSChristopher Meis }
4437e446a40SChristopher Meis
4447e446a40SChristopher Meis co_return true;
4457e446a40SChristopher Meis }
4467e446a40SChristopher Meis
lineSplit(char ** dest,char * src,char * delim)4477e446a40SChristopher Meis int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
4487e446a40SChristopher Meis {
4497e446a40SChristopher Meis char* s = strtok(src, delim);
4507e446a40SChristopher Meis int size = 0;
4517e446a40SChristopher Meis int maxSz = 5;
4527e446a40SChristopher Meis
4537e446a40SChristopher Meis while (s)
4547e446a40SChristopher Meis {
4557e446a40SChristopher Meis *dest++ = s;
4567e446a40SChristopher Meis if ((++size) >= maxSz)
4577e446a40SChristopher Meis {
4587e446a40SChristopher Meis break;
4597e446a40SChristopher Meis }
4607e446a40SChristopher Meis s = strtok(NULL, delim);
4617e446a40SChristopher Meis }
4627e446a40SChristopher Meis
4637e446a40SChristopher Meis return size;
4647e446a40SChristopher Meis }
4657e446a40SChristopher Meis
parseImage(const uint8_t * image,size_t image_size)466*fd34144bSChristopher Meis bool XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
4677e446a40SChristopher Meis {
4687e446a40SChristopher Meis size_t lenEndTag = strlen(DataEndTag);
4697e446a40SChristopher Meis size_t lenStartTag = strlen(DataStartTag);
4707e446a40SChristopher Meis size_t lenComment = strlen(DataComment);
4717e446a40SChristopher Meis size_t lenXV = strlen(DataXV);
4727e446a40SChristopher Meis size_t start = 0;
4737e446a40SChristopher Meis const int maxLineLength = 40;
4747e446a40SChristopher Meis char line[maxLineLength];
4757e446a40SChristopher Meis char* token = NULL;
4767e446a40SChristopher Meis bool isData = false;
4777e446a40SChristopher Meis char delim = ' ';
4787e446a40SChristopher Meis uint16_t offset;
4797e446a40SChristopher Meis uint8_t sectType = 0x0;
4807e446a40SChristopher Meis uint32_t dWord;
4817e446a40SChristopher Meis int dataCnt = 0;
4827e446a40SChristopher Meis int sectIndex = -1;
4837e446a40SChristopher Meis
4847e446a40SChristopher Meis for (size_t i = 0; i < image_size; i++)
4857e446a40SChristopher Meis {
4867e446a40SChristopher Meis if (image[i] == '\n')
4877e446a40SChristopher Meis {
4887e446a40SChristopher Meis std::memcpy(line, image + start, i - start);
4897e446a40SChristopher Meis if (!strncmp(line, DataComment, lenComment))
4907e446a40SChristopher Meis {
4917e446a40SChristopher Meis token = line + lenComment;
4927e446a40SChristopher Meis if (!strncmp(token, DataXV, lenXV))
4937e446a40SChristopher Meis {
4947e446a40SChristopher Meis debug("Parsing: {OBJ}", "OBJ",
4957e446a40SChristopher Meis reinterpret_cast<const char*>(line));
4967e446a40SChristopher Meis }
4977e446a40SChristopher Meis start = i + 1;
4987e446a40SChristopher Meis continue;
4997e446a40SChristopher Meis }
5007e446a40SChristopher Meis if (!strncmp(line, DataEndTag, lenEndTag))
5017e446a40SChristopher Meis {
5027e446a40SChristopher Meis debug("Parsing: {OBJ}", "OBJ",
5037e446a40SChristopher Meis reinterpret_cast<const char*>(line));
5047e446a40SChristopher Meis break;
5057e446a40SChristopher Meis }
5067e446a40SChristopher Meis else if (isData)
5077e446a40SChristopher Meis {
5087e446a40SChristopher Meis char* tokenList[8] = {0};
5097e446a40SChristopher Meis int tokenSize = lineSplit(tokenList, line, &delim);
5107e446a40SChristopher Meis if (tokenSize < 1)
5117e446a40SChristopher Meis {
5127e446a40SChristopher Meis start = i + 1;
5137e446a40SChristopher Meis continue;
5147e446a40SChristopher Meis }
5157e446a40SChristopher Meis
5167e446a40SChristopher Meis offset = (uint16_t)strtol(tokenList[0], NULL, 16);
5177e446a40SChristopher Meis if (sectType == SectTrim && offset != 0x0)
5187e446a40SChristopher Meis {
5197e446a40SChristopher Meis continue;
5207e446a40SChristopher Meis }
5217e446a40SChristopher Meis
5227e446a40SChristopher Meis for (int i = 1; i < tokenSize; i++)
5237e446a40SChristopher Meis {
5247e446a40SChristopher Meis dWord = (uint32_t)strtol(tokenList[i], NULL, 16);
5257e446a40SChristopher Meis if ((offset == 0x0) && (i == 1))
5267e446a40SChristopher Meis {
5277e446a40SChristopher Meis sectType = (uint8_t)dWord;
5287e446a40SChristopher Meis if (sectType == SectTrim)
5297e446a40SChristopher Meis {
5307e446a40SChristopher Meis break;
5317e446a40SChristopher Meis }
5327e446a40SChristopher Meis if ((++sectIndex) >= MaxSectCnt)
5337e446a40SChristopher Meis {
534*fd34144bSChristopher Meis return false;
5357e446a40SChristopher Meis }
5367e446a40SChristopher Meis
5377e446a40SChristopher Meis configuration.section[sectIndex].type = sectType;
5387e446a40SChristopher Meis configuration.sectCnt = sectIndex + 1;
5397e446a40SChristopher Meis dataCnt = 0;
5407e446a40SChristopher Meis }
5417e446a40SChristopher Meis
5427e446a40SChristopher Meis if (dataCnt >= MaxSectDataCnt)
5437e446a40SChristopher Meis {
544*fd34144bSChristopher Meis return false;
5457e446a40SChristopher Meis }
5467e446a40SChristopher Meis
5477e446a40SChristopher Meis configuration.section[sectIndex].data[dataCnt++] = dWord;
5487e446a40SChristopher Meis configuration.section[sectIndex].dataCnt = dataCnt;
5497e446a40SChristopher Meis configuration.totalCnt++;
5507e446a40SChristopher Meis }
5517e446a40SChristopher Meis }
5527e446a40SChristopher Meis else
5537e446a40SChristopher Meis {
5547e446a40SChristopher Meis if ((token = strstr(line, AddressField)) != NULL)
5557e446a40SChristopher Meis {
5567e446a40SChristopher Meis if ((token = strstr(token, "0x")) != NULL)
5577e446a40SChristopher Meis {
5587e446a40SChristopher Meis configuration.addr =
5597e446a40SChristopher Meis (uint8_t)(strtoul(token, NULL, 16) << 1);
5607e446a40SChristopher Meis }
5617e446a40SChristopher Meis }
5627e446a40SChristopher Meis else if ((token = strstr(line, ChecksumField)) != NULL)
5637e446a40SChristopher Meis {
5647e446a40SChristopher Meis if ((token = strstr(token, "0x")) != NULL)
5657e446a40SChristopher Meis {
5667e446a40SChristopher Meis configuration.sumExp =
5677e446a40SChristopher Meis (uint32_t)strtoul(token, NULL, 16);
5687e446a40SChristopher Meis }
5697e446a40SChristopher Meis }
5707e446a40SChristopher Meis else if (!strncmp(line, DataStartTag, lenStartTag))
5717e446a40SChristopher Meis {
5727e446a40SChristopher Meis isData = true;
5737e446a40SChristopher Meis start = i + 1;
5747e446a40SChristopher Meis continue;
5757e446a40SChristopher Meis }
5767e446a40SChristopher Meis else
5777e446a40SChristopher Meis {
5787e446a40SChristopher Meis start = i + 1;
5797e446a40SChristopher Meis continue;
5807e446a40SChristopher Meis }
5817e446a40SChristopher Meis }
5827e446a40SChristopher Meis start = i + 1;
5837e446a40SChristopher Meis }
5847e446a40SChristopher Meis }
5857e446a40SChristopher Meis
586*fd34144bSChristopher Meis return true;
5877e446a40SChristopher Meis }
5887e446a40SChristopher Meis
checkImage()589*fd34144bSChristopher Meis bool XDPE1X2XX::checkImage()
5907e446a40SChristopher Meis {
5917e446a40SChristopher Meis uint8_t i;
5927e446a40SChristopher Meis uint32_t crc;
5937e446a40SChristopher Meis uint32_t sum = 0;
5947e446a40SChristopher Meis
5957e446a40SChristopher Meis for (i = 0; i < configuration.sectCnt; i++)
5967e446a40SChristopher Meis {
5977e446a40SChristopher Meis struct configSect* sect = &configuration.section[i];
5987e446a40SChristopher Meis if (sect == NULL)
5997e446a40SChristopher Meis {
6007e446a40SChristopher Meis error("Failed to check image - unexpected NULL section");
601*fd34144bSChristopher Meis return false;
6027e446a40SChristopher Meis }
6037e446a40SChristopher Meis
604*fd34144bSChristopher Meis crc = calcCRC32(§->data[0], 2);
6057e446a40SChristopher Meis if (crc != sect->data[2])
6067e446a40SChristopher Meis {
6077e446a40SChristopher Meis error("Failed to check image - first CRC value mismatch");
608*fd34144bSChristopher Meis return false;
6097e446a40SChristopher Meis }
6107e446a40SChristopher Meis sum += crc;
6117e446a40SChristopher Meis
6127e446a40SChristopher Meis // check CRC of section data
6137e446a40SChristopher Meis crc = calcCRC32(§->data[3], sect->dataCnt - 4);
6147e446a40SChristopher Meis if (crc != sect->data[sect->dataCnt - 1])
6157e446a40SChristopher Meis {
6167e446a40SChristopher Meis error("Failed to check image - second CRC value mismatch");
617*fd34144bSChristopher Meis return false;
6187e446a40SChristopher Meis }
6197e446a40SChristopher Meis sum += crc;
6207e446a40SChristopher Meis }
6217e446a40SChristopher Meis
6227e446a40SChristopher Meis if (sum != configuration.sumExp)
6237e446a40SChristopher Meis {
624*fd34144bSChristopher Meis debug("Calculated CRC: {CRC}", "CRC", lg2::hex, sum);
625*fd34144bSChristopher Meis debug("Config CRC {CRC}", "CRC", lg2::hex, configuration.sumExp);
6267e446a40SChristopher Meis error("Failed to check image - third CRC value mismatch");
627*fd34144bSChristopher Meis return false;
6287e446a40SChristopher Meis }
6297e446a40SChristopher Meis
630*fd34144bSChristopher Meis return true;
6317e446a40SChristopher Meis }
6327e446a40SChristopher Meis
verifyImage(const uint8_t * image,size_t imageSize)6337e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
6347e446a40SChristopher Meis size_t imageSize)
6357e446a40SChristopher Meis {
636*fd34144bSChristopher Meis if (!parseImage(image, imageSize))
6377e446a40SChristopher Meis {
6387e446a40SChristopher Meis error("Failed to update firmware on parsing Image");
6397e446a40SChristopher Meis co_return false;
6407e446a40SChristopher Meis }
6417e446a40SChristopher Meis
642*fd34144bSChristopher Meis if (!checkImage())
6437e446a40SChristopher Meis {
6447e446a40SChristopher Meis error("Failed to update firmware on check image");
6457e446a40SChristopher Meis co_return false;
6467e446a40SChristopher Meis }
6477e446a40SChristopher Meis
6487e446a40SChristopher Meis co_return true;
6497e446a40SChristopher Meis }
6507e446a40SChristopher Meis
getScratchPadAddress()651*fd34144bSChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::getScratchPadAddress()
6527e446a40SChristopher Meis {
653*fd34144bSChristopher Meis uint8_t tbuf[16] = {0};
654*fd34144bSChristopher Meis uint8_t rbuf[16] = {0};
655*fd34144bSChristopher Meis
656*fd34144bSChristopher Meis tbuf[0] = 0x02;
657*fd34144bSChristopher Meis tbuf[1] = 0x00;
658*fd34144bSChristopher Meis tbuf[2] = 0x00;
659*fd34144bSChristopher Meis tbuf[3] = 0x00;
660*fd34144bSChristopher Meis
661*fd34144bSChristopher Meis if (!(co_await mfrFWcmd(MFRFwCmdGetHWAddress, MFRGetHWAddressWaitTime, tbuf,
662*fd34144bSChristopher Meis rbuf)))
6637e446a40SChristopher Meis {
664*fd34144bSChristopher Meis error("mfrFWcmd call failed to retrieve scratchpad address");
6657e446a40SChristopher Meis co_return false;
6667e446a40SChristopher Meis }
6677e446a40SChristopher Meis
668*fd34144bSChristopher Meis info.scratchPadAddress = (static_cast<uint32_t>(rbuf[3]) << 24) |
669*fd34144bSChristopher Meis (static_cast<uint32_t>(rbuf[2]) << 16) |
670*fd34144bSChristopher Meis (static_cast<uint32_t>(rbuf[1]) << 8) |
671*fd34144bSChristopher Meis (static_cast<uint32_t>(rbuf[0]));
672*fd34144bSChristopher Meis
673*fd34144bSChristopher Meis debug("Scratchpad Address: {ADDR}", "ADDR", lg2::hex,
674*fd34144bSChristopher Meis info.scratchPadAddress);
675*fd34144bSChristopher Meis
676*fd34144bSChristopher Meis co_return true;
677*fd34144bSChristopher Meis }
678*fd34144bSChristopher Meis
updateFirmware(bool force)679*fd34144bSChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
680*fd34144bSChristopher Meis {
681*fd34144bSChristopher Meis bool ret = true;
682*fd34144bSChristopher Meis if (!(co_await getScratchPadAddress()))
683*fd34144bSChristopher Meis {
684*fd34144bSChristopher Meis error("Failed to retrieve scratchpad address");
685*fd34144bSChristopher Meis co_return false;
686*fd34144bSChristopher Meis }
687*fd34144bSChristopher Meis
688*fd34144bSChristopher Meis // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
689*fd34144bSChristopher Meis ret = co_await program(force);
690*fd34144bSChristopher Meis if (!ret)
691*fd34144bSChristopher Meis // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
692*fd34144bSChristopher Meis {
693*fd34144bSChristopher Meis error("Failed to update firmware on program");
694*fd34144bSChristopher Meis }
695*fd34144bSChristopher Meis
696*fd34144bSChristopher Meis info.deviceId = 0;
697*fd34144bSChristopher Meis info.deviceRev = 0;
698*fd34144bSChristopher Meis info.remainingWrites = 0;
699*fd34144bSChristopher Meis info.scratchPadAddress = 0;
700*fd34144bSChristopher Meis info.actualCRC = 0;
701*fd34144bSChristopher Meis info.configSize = 0;
702*fd34144bSChristopher Meis
7037e446a40SChristopher Meis // Reset the configuration
7047e446a40SChristopher Meis configuration.addr = 0;
7057e446a40SChristopher Meis configuration.totalCnt = 0;
7067e446a40SChristopher Meis configuration.sumExp = 0;
7077e446a40SChristopher Meis configuration.sectCnt = 0;
7087e446a40SChristopher Meis for (int i = 0; i <= MaxSectCnt - 1; i++)
7097e446a40SChristopher Meis {
7107e446a40SChristopher Meis configuration.section[i].type = 0;
7117e446a40SChristopher Meis configuration.section[i].dataCnt = 0;
7127e446a40SChristopher Meis for (int j = 0; j <= MaxSectDataCnt; j++)
7137e446a40SChristopher Meis {
7147e446a40SChristopher Meis configuration.section[i].data[j] = 0;
7157e446a40SChristopher Meis }
7167e446a40SChristopher Meis }
7177e446a40SChristopher Meis
718*fd34144bSChristopher Meis if (!ret)
719*fd34144bSChristopher Meis {
720*fd34144bSChristopher Meis co_return false;
721*fd34144bSChristopher Meis }
722*fd34144bSChristopher Meis
7237e446a40SChristopher Meis co_return true;
7247e446a40SChristopher Meis }
7257e446a40SChristopher Meis
reset()7267e446a40SChristopher Meis sdbusplus::async::task<bool> XDPE1X2XX::reset()
7277e446a40SChristopher Meis {
728*fd34144bSChristopher Meis if (!(co_await mfrFWcmd(MFRFwCmdReset, VRResetDelay, NULL, NULL)))
7297e446a40SChristopher Meis {
7307e446a40SChristopher Meis error("Failed to reset the VR");
7317e446a40SChristopher Meis co_return false;
7327e446a40SChristopher Meis }
7337e446a40SChristopher Meis
7347e446a40SChristopher Meis co_return true;
7357e446a40SChristopher Meis }
7367e446a40SChristopher Meis
calcCRC32(const uint32_t * data,int len)7377e446a40SChristopher Meis uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
7387e446a40SChristopher Meis {
7397e446a40SChristopher Meis if (data == NULL)
7407e446a40SChristopher Meis {
7417e446a40SChristopher Meis return 0;
7427e446a40SChristopher Meis }
7437e446a40SChristopher Meis
7447e446a40SChristopher Meis uint32_t crc = 0xFFFFFFFF;
7457e446a40SChristopher Meis for (int i = 0; i < len; i++)
7467e446a40SChristopher Meis {
7477e446a40SChristopher Meis crc ^= data[i];
7487e446a40SChristopher Meis
7497e446a40SChristopher Meis for (int b = 0; b < 32; b++)
7507e446a40SChristopher Meis {
7517e446a40SChristopher Meis if (crc & 0x1)
7527e446a40SChristopher Meis {
7537e446a40SChristopher Meis crc = (crc >> 1) ^ CRC32Poly; // lsb-first
7547e446a40SChristopher Meis }
7557e446a40SChristopher Meis else
7567e446a40SChristopher Meis {
7577e446a40SChristopher Meis crc >>= 1;
7587e446a40SChristopher Meis }
7597e446a40SChristopher Meis }
7607e446a40SChristopher Meis }
7617e446a40SChristopher Meis
7627e446a40SChristopher Meis return ~crc;
7637e446a40SChristopher Meis }
7647e446a40SChristopher Meis
forcedUpdateAllowed()7657e446a40SChristopher Meis bool XDPE1X2XX::forcedUpdateAllowed()
7667e446a40SChristopher Meis {
7677e446a40SChristopher Meis return true;
7687e446a40SChristopher Meis }
7697e446a40SChristopher Meis
7707e446a40SChristopher Meis } // namespace phosphor::software::VR
771