1 #include "mp2x6xx.hpp" 2 3 #include "common/include/utils.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 PHOSPHOR_LOG2_USING; 8 9 namespace phosphor::software::VR 10 { 11 12 static constexpr size_t vendorIdLength = 3; 13 static constexpr size_t deviceIdLength = 4; 14 static constexpr size_t configIdLength = 2; 15 static constexpr size_t statusByteLength = 1; 16 static constexpr size_t crcLength = 2; 17 18 static constexpr std::string_view productIdRegName = "TRIM_MFR_PRODUCT_ID2"; 19 static constexpr std::string_view crcUserRegName = "CRC_USER"; 20 21 static constexpr uint8_t pageMask = 0x0F; 22 static constexpr uint8_t configMask = 0xF0; 23 24 static constexpr uint8_t disableWriteProtect = 0x00; 25 static constexpr uint16_t disablePage2WriteProtect = 0x128C; 26 static constexpr uint16_t disablePage3WriteProtect = 0x0082; 27 28 enum class MP2X6XXCmd : uint8_t 29 { 30 // Page 0 commands 31 readCRCReg = 0xED, 32 // Page 1 commands 33 mfrMTPMemoryCtrl = 0xCC, 34 // Page 2 commands 35 selectConfigCtrl = 0x1A, 36 // Page 3 commands 37 mfrMTPMemoryCtrlPage3 = 0x81, 38 }; 39 40 sdbusplus::async::task<bool> MP2X6XX::parseDeviceConfiguration() 41 { 42 if (!configuration) 43 { 44 error("Device configuration not initialized"); 45 co_return false; 46 } 47 48 configuration->vendorId = 0x4D5053; 49 50 for (const auto& tokens : parser->lineTokens) 51 { 52 if (!parser->isValidDataTokens(tokens)) 53 { 54 continue; 55 } 56 57 auto regName = parser->getVal<std::string>(tokens, ATE::regName); 58 if (regName == productIdRegName) 59 { 60 configuration->productId = 61 parser->getVal<uint32_t>(tokens, ATE::regDataHex); 62 configuration->configId = 63 parser->getVal<uint32_t>(tokens, ATE::configId); 64 } 65 else if (regName == crcUserRegName) 66 { 67 configuration->crcUser = 68 parser->getVal<uint32_t>(tokens, ATE::regDataHex); 69 break; 70 } 71 } 72 73 co_return true; 74 } 75 76 sdbusplus::async::task<bool> MP2X6XX::verifyImage(const uint8_t* image, 77 size_t imageSize) 78 { 79 if (!co_await parseImage(image, imageSize, MPSImageType::type1)) 80 { 81 error("Image verification failed: image parsing failed"); 82 co_return false; 83 } 84 85 if (configuration->registersData.empty()) 86 { 87 error("Image verification failed - no data found"); 88 co_return false; 89 } 90 91 if (configuration->productId == 0 || configuration->configId == 0) 92 { 93 error("Image verification failed - missing product or config ID"); 94 co_return false; 95 } 96 97 co_return true; 98 } 99 100 sdbusplus::async::task<bool> MP2X6XX::checkId(PMBusCmd pmBusCmd, 101 uint32_t expected) 102 { 103 const uint8_t cmd = static_cast<uint8_t>(pmBusCmd); 104 size_t idLen = 0; 105 bool blockRead = false; 106 107 switch (pmBusCmd) 108 { 109 case PMBusCmd::mfrId: 110 idLen = vendorIdLength; 111 blockRead = true; 112 break; 113 case PMBusCmd::icDeviceId: 114 idLen = deviceIdLength; 115 blockRead = true; 116 break; 117 case PMBusCmd::mfrSerial: 118 idLen = configIdLength; 119 break; 120 default: 121 error("Invalid command for ID check: {CMD}", "CMD", lg2::hex, cmd); 122 co_return false; 123 } 124 125 std::vector<uint8_t> rbuf; 126 std::vector<uint8_t> tbuf; 127 128 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 129 if (!i2cInterface.sendReceive(tbuf, rbuf)) 130 { 131 error("Failed to set page 0 for ID check"); 132 co_return false; 133 } 134 135 tbuf = {cmd}; 136 rbuf.resize(idLen + (blockRead ? statusByteLength : 0)); 137 138 if (!i2cInterface.sendReceive(tbuf, rbuf)) 139 { 140 error("I2C failure during ID check, cmd {CMD}", "CMD", lg2::hex, cmd); 141 co_return false; 142 } 143 144 auto idBytes = std::span(rbuf).subspan(blockRead ? statusByteLength : 0); 145 uint32_t id = bytesToInt<uint32_t>(idBytes); 146 147 if (id != expected) 148 { 149 error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD", 150 lg2::hex, cmd, "ID", lg2::hex, id, "EXP", lg2::hex, expected); 151 co_return false; 152 } 153 154 co_return true; 155 } 156 157 sdbusplus::async::task<bool> MP2X6XX::unlockWriteProtect() 158 { 159 std::vector<uint8_t> tbuf; 160 std::vector<uint8_t> rbuf; 161 162 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 163 if (!i2cInterface.sendReceive(tbuf, rbuf)) 164 { 165 error("Failed to set page 0 for unlocking write protect"); 166 co_return false; 167 } 168 169 tbuf = buildByteVector(PMBusCmd::writeProtect, disableWriteProtect); 170 if (!i2cInterface.sendReceive(tbuf, rbuf)) 171 { 172 error("Failed to disable write protect"); 173 co_return false; 174 } 175 176 // unlock page 2 write protect 177 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1); 178 if (!i2cInterface.sendReceive(tbuf, rbuf)) 179 { 180 error("Failed to set page 1 for unlocking write protect for page 2"); 181 co_return false; 182 } 183 184 tbuf = 185 buildByteVector(MP2X6XXCmd::mfrMTPMemoryCtrl, disablePage2WriteProtect); 186 if (!i2cInterface.sendReceive(tbuf, rbuf)) 187 { 188 error("Failed to unlock page 2 write protect"); 189 co_return false; 190 } 191 192 // unlock page 3 write protect 193 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page3); 194 if (!i2cInterface.sendReceive(tbuf, rbuf)) 195 { 196 error("Failed to set page 3 for unlocking write protect for page 3"); 197 co_return false; 198 } 199 200 tbuf = buildByteVector(MP2X6XXCmd::mfrMTPMemoryCtrlPage3, 201 disablePage3WriteProtect); 202 if (!i2cInterface.sendReceive(tbuf, rbuf)) 203 { 204 error("Failed to unlock page 3 write protect"); 205 co_return false; 206 } 207 208 debug("Unlocked write protect"); 209 210 co_return true; 211 } 212 213 sdbusplus::async::task<bool> MP2X6XX::selectConfig(uint8_t config) 214 { 215 // MPS config select command: 216 // Writes to Page 2 @ 0x1A: value = 0x0F00 | ((config + 7) << 4) 217 // For config 1–6 → result: 0x0F80 to 0x0FD0 218 219 std::vector<uint8_t> tbuf; 220 std::vector<uint8_t> rbuf; 221 222 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2); 223 if (!i2cInterface.sendReceive(tbuf, rbuf)) 224 { 225 error("Failed to set page 2 for configuration switch"); 226 co_return false; 227 } 228 229 constexpr uint8_t baseOffset = 7; 230 uint8_t encodedNibble = static_cast<uint8_t>((config + baseOffset) << 4); 231 uint16_t command = 0x0F00 | encodedNibble; 232 233 tbuf = buildByteVector(MP2X6XXCmd::selectConfigCtrl, command); 234 235 if (!i2cInterface.sendReceive(tbuf, rbuf)) 236 { 237 error("Failed to write config select command {CMD} for config {CONFIG}", 238 "CMD", lg2::hex, command, "CONFIG", config); 239 co_return false; 240 } 241 242 debug("Switched to config {CONFIG}", "CONFIG", config); 243 co_return true; 244 } 245 246 sdbusplus::async::task<bool> MP2X6XX::programConfigData( 247 const std::vector<MPSData>& gdata) 248 { 249 std::vector<uint8_t> tbuf; 250 std::vector<uint8_t> rbuf; 251 252 for (const auto& data : gdata) 253 { 254 uint8_t page = data.page & pageMask; 255 256 tbuf = buildByteVector(PMBusCmd::page, page); 257 if (!i2cInterface.sendReceive(tbuf, rbuf)) 258 { 259 error("Failed to set page {PAGE} for register {REG}", "PAGE", page, 260 "REG", lg2::hex, data.addr); 261 co_return false; 262 } 263 264 tbuf = {data.addr}; 265 tbuf.insert(tbuf.end(), data.data.begin(), 266 data.data.begin() + data.length); 267 268 if (!i2cInterface.sendReceive(tbuf, rbuf)) 269 { 270 error( 271 "Failed to write data {DATA} to register {REG} on page {PAGE}", 272 "DATA", lg2::hex, bytesToInt<uint32_t>(data.data), "REG", 273 lg2::hex, data.addr, "PAGE", page); 274 co_return false; 275 } 276 } 277 278 if (!co_await storeUserCode()) 279 { 280 error("Failed to store user code after programming config data"); 281 co_return false; 282 } 283 284 co_return true; 285 } 286 287 sdbusplus::async::task<bool> MP2X6XX::configAllRegisters() 288 { 289 for (const auto& [config, gdata] : getGroupedConfigData(configMask, 4)) 290 { 291 debug("Configuring registers for config {CONFIG}", "CONFIG", config); 292 293 // Select the appropriate config before programming its registers 294 if (config > 0 && !co_await selectConfig(config)) 295 { 296 co_return false; 297 } 298 299 if (!co_await programConfigData(gdata)) 300 { 301 error("Failed to program configuration {CONFIG}", "CONFIG", config); 302 co_return false; 303 } 304 305 debug("Configured {SIZE} registers for config {CONFIG}", "SIZE", 306 gdata.size(), "CONFIG", config); 307 } 308 309 co_return true; 310 } 311 312 sdbusplus::async::task<bool> MP2X6XX::storeUserCode() 313 { 314 std::vector<uint8_t> tbuf; 315 std::vector<uint8_t> rbuf; 316 317 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 318 if (!i2cInterface.sendReceive(tbuf, rbuf)) 319 { 320 error("Failed to set page 0 for storing user code"); 321 co_return false; 322 } 323 324 tbuf = buildByteVector(PMBusCmd::storeUserCode); 325 if (!i2cInterface.sendReceive(tbuf, rbuf)) 326 { 327 error("Failed to store user code"); 328 co_return false; 329 } 330 331 // Wait store user code 332 co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500)); 333 334 debug("Stored user code"); 335 336 co_return true; 337 } 338 339 sdbusplus::async::task<bool> MP2X6XX::getCRC(uint32_t* checksum) 340 { 341 if (checksum == nullptr) 342 { 343 error("getCRC() called with null checksum pointer"); 344 co_return false; 345 } 346 347 std::vector<uint8_t> tbuf; 348 std::vector<uint8_t> rbuf; 349 350 tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0); 351 if (!i2cInterface.sendReceive(tbuf, rbuf)) 352 { 353 error("Failed to set page 0 for CRC read"); 354 co_return false; 355 } 356 357 tbuf = buildByteVector(MP2X6XXCmd::readCRCReg); 358 rbuf.resize(crcLength); 359 if (!i2cInterface.sendReceive(tbuf, rbuf)) 360 { 361 error("Failed to read CRC from device"); 362 co_return false; 363 } 364 365 if (rbuf.size() < crcLength) 366 { 367 error("CRC read returned insufficient data"); 368 co_return false; 369 } 370 371 *checksum = bytesToInt<uint32_t>(rbuf); 372 373 debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum); 374 375 co_return true; 376 } 377 378 sdbusplus::async::task<bool> MP2X6XX::checkMTPCRC() 379 { 380 uint32_t crc = 0; 381 // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch) 382 if (!co_await getCRC(&crc)) 383 // NOLINTEND(clang-analyzer-core.uninitialized.Branch) 384 { 385 error("Failed to get CRC for MTP check"); 386 co_return false; 387 } 388 389 debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP", 390 lg2::hex, configuration->crcUser); 391 392 co_return configuration->crcUser == crc; 393 } 394 395 bool MP2X6XX::forcedUpdateAllowed() 396 { 397 return true; 398 } 399 400 sdbusplus::async::task<bool> MP2X6XX::updateFirmware(bool force) 401 { 402 (void)force; 403 404 if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId)) 405 { 406 co_return false; 407 } 408 409 if (!co_await checkId(PMBusCmd::icDeviceId, configuration->productId)) 410 { 411 co_return false; 412 } 413 414 if (!co_await checkId(PMBusCmd::mfrSerial, configuration->configId)) 415 { 416 co_return false; 417 } 418 419 if (!co_await unlockWriteProtect()) 420 { 421 co_return false; 422 } 423 424 if (!co_await configAllRegisters()) 425 { 426 co_return false; 427 } 428 429 if (!(co_await checkMTPCRC())) 430 { 431 co_return false; 432 } 433 434 co_return true; 435 } 436 437 } // namespace phosphor::software::VR 438