1 #pragma once 2 3 #include "common/include/i2c/i2c.hpp" 4 #include "i2c-vr/vr.hpp" 5 6 #include <phosphor-logging/lg2.hpp> 7 8 #include <cstdint> 9 #include <iterator> 10 #include <memory> 11 #include <string_view> 12 #include <vector> 13 14 namespace phosphor::software::VR 15 { 16 17 /** 18 * @brief 19 * Columns of an Automated Test Equipment (ATE) format configuration file. 20 * Each enumerator corresponds to a tab-separated column index. 21 */ 22 enum class ATE : uint8_t 23 { 24 configId = 0, 25 pageNum, 26 regAddrHex, 27 regAddrDec, 28 regName, 29 regDataHex, 30 regDataDec, 31 writeType, 32 colCount, 33 }; 34 35 enum class MPSPage : uint8_t 36 { 37 page0 = 0, 38 page1, 39 page2, 40 page3, 41 page4, 42 page29 = 0x29, 43 page2A = 0x2A, 44 }; 45 46 struct MPSData 47 { 48 uint8_t page = 0; 49 uint8_t addr = 0; 50 uint8_t length = 0; 51 std::array<uint8_t, 4> data{}; 52 }; 53 54 struct MPSConfig 55 { 56 uint32_t vendorId = 0; 57 uint32_t productId = 0; 58 uint32_t configId = 0; 59 uint32_t crcUser = 0; 60 uint32_t crcMulti = 0; 61 std::vector<MPSData> registersData; 62 }; 63 64 /** 65 * @brief 66 * Utility class to iterate over lines and tokenize them by tab characters. 67 */ 68 class TokenizedLines 69 { 70 public: TokenizedLines(const uint8_t * d,size_t s)71 TokenizedLines(const uint8_t* d, size_t s) : 72 data(reinterpret_cast<const char*>(d), s) 73 {} 74 /** 75 * @brief Iterator over tokenized lines. 76 */ 77 struct Iterator 78 { 79 using iterator_category = std::forward_iterator_tag; 80 using value_type = std::vector<std::string_view>; 81 using difference_type = std::ptrdiff_t; 82 using pointer = const value_type*; 83 using reference = const value_type&; 84 85 Iterator() = default; // End iterator Iteratorphosphor::software::VR::TokenizedLines::Iterator86 Iterator(std::string_view sv) : remaining(sv) 87 { 88 next(); 89 } 90 operator *phosphor::software::VR::TokenizedLines::Iterator91 reference operator*() const 92 { 93 return currentTokens; 94 } 95 operator ->phosphor::software::VR::TokenizedLines::Iterator96 pointer operator->() const 97 { 98 return ¤tTokens; 99 } 100 operator ++phosphor::software::VR::TokenizedLines::Iterator101 Iterator& operator++() 102 { 103 next(); 104 return *this; 105 } 106 operator ++phosphor::software::VR::TokenizedLines::Iterator107 Iterator operator++(int) 108 { 109 auto result = *this; 110 ++(*this); 111 return result; 112 } 113 operator ==(const Iterator & a,const Iterator & b)114 friend bool operator==(const Iterator& a, const Iterator& b) 115 { 116 return a.remaining.empty() && b.remaining.empty(); 117 } 118 operator !=(const Iterator & a,const Iterator & b)119 friend bool operator!=(const Iterator& a, const Iterator& b) 120 { 121 return !(a == b); 122 } 123 124 private: 125 std::string_view remaining; 126 std::vector<std::string_view> currentTokens; 127 nextphosphor::software::VR::TokenizedLines::Iterator128 void next() 129 { 130 currentTokens.clear(); 131 if (remaining.empty()) 132 { 133 return; 134 } 135 136 // Extract current line 137 auto newlinePos = remaining.find('\n'); 138 std::string_view line = remaining.substr(0, newlinePos); 139 remaining = (newlinePos == std::string_view::npos) 140 ? std::string_view{} 141 : remaining.substr(newlinePos + 1); 142 143 // Tokenize by tab 144 size_t start = 0; 145 while (start < line.size()) 146 { 147 start = line.find_first_not_of('\t', start); 148 if (start == std::string_view::npos) 149 { 150 break; 151 } 152 153 auto end = line.find('\t', start); 154 currentTokens.emplace_back(line.substr(start, end - start)); 155 start = (end == std::string_view::npos) ? line.size() : end; 156 } 157 } 158 }; 159 begin() const160 Iterator begin() const 161 { 162 return Iterator(data); 163 } 164 end()165 static Iterator end() 166 { 167 return Iterator(); 168 } 169 170 private: 171 std::string_view data; 172 }; 173 174 /** 175 * @brief Base parser for MPS configuration images. 176 */ 177 class MPSImageParser 178 { 179 public: MPSImageParser(const uint8_t * image,size_t imageSize)180 MPSImageParser(const uint8_t* image, size_t imageSize) : 181 lineTokens(image, imageSize) 182 {} 183 184 template <typename> 185 inline static constexpr bool always_false = false; 186 187 /** 188 * @brief Extract a typed value from a tokenized line. 189 * @tparam T Return type (string or integral type) 190 * @param tokens Tokenized line 191 * @param index Column index (ATE enum) 192 * @return Parsed value or default if invalid 193 */ 194 template <typename T> getVal(const std::vector<std::string_view> & tokens,ATE index)195 T getVal(const std::vector<std::string_view>& tokens, ATE index) 196 { 197 size_t idx = static_cast<size_t>(index); 198 199 if (tokens.size() <= idx) 200 { 201 lg2::error("Index out of range for ATE enum: {INDEX}", "INDEX", 202 static_cast<uint32_t>(idx)); 203 return T{}; 204 } 205 206 std::string_view token = tokens[idx]; 207 208 if constexpr (std::is_same_v<T, std::string>) 209 { 210 return std::string(token); 211 } 212 else if constexpr (std::is_integral_v<T>) 213 { 214 unsigned long val = 0; 215 try 216 { 217 val = std::stoul(std::string(token), nullptr, 16); 218 } 219 catch (...) 220 { 221 lg2::error("Invalid hex value: {INDEX}", "INDEX", 222 static_cast<uint32_t>(idx)); 223 return T{}; 224 } 225 return static_cast<T>(val); 226 } 227 else 228 { 229 static_assert(always_false<T>, "Unsupported type in getVal"); 230 } 231 } 232 233 /** 234 * @brief Check if a tokenized line contains valid register data. 235 */ 236 static bool isValidDataTokens(const std::vector<std::string_view>& tokens); 237 238 /** 239 * @brief Convert tokenized line into MPSData structure. 240 */ 241 MPSData extractData(const std::vector<std::string_view>& tokens); 242 243 /** 244 * @brief Collect all register data entries from the parsed image. 245 */ 246 std::vector<MPSData> getRegistersData(); 247 248 TokenizedLines lineTokens; 249 }; 250 251 /** 252 * @brief Base class for MPS Voltage Regulators. 253 */ 254 class MPSVoltageRegulator : public VoltageRegulator 255 { 256 public: MPSVoltageRegulator(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address)257 MPSVoltageRegulator(sdbusplus::async::context& ctx, uint16_t bus, 258 uint16_t address) : 259 VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)) 260 {} 261 262 /** 263 * @brief Parse device-specific configuration from the loaded image. 264 * @return async task returning true if parsing succeeds 265 */ 266 virtual sdbusplus::async::task<bool> parseDeviceConfiguration() = 0; 267 268 /** 269 * @brief Parse an image file into internal MPS configuration. 270 * @param image Pointer to the image data 271 * @param imageSize Size of the image data 272 * @return async task returning true if parsing succeeds 273 */ 274 sdbusplus::async::task<bool> parseImage(const uint8_t* image, 275 size_t imageSize); 276 277 /** 278 * @brief Group register data by page, optionally masked and shifted. 279 * @param configMask Bitmask to select relevant page bits (default 0xFF) 280 * @param shift Number of bits to shift masked value to obtain group key 281 * @return map of page keys to vector of MPSData 282 */ 283 std::map<uint8_t, std::vector<MPSData>> getGroupedConfigData( 284 uint8_t configMask = 0xFF, uint8_t shift = 0); 285 286 protected: 287 phosphor::i2c::I2C i2cInterface; 288 std::unique_ptr<MPSImageParser> parser; 289 std::unique_ptr<MPSConfig> configuration; 290 }; 291 292 } // namespace phosphor::software::VR 293