xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mps.hpp (revision c284e75bd121f38f11a362e7a8679c3b455d07b8)
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 /**
36  * @brief Represents the format of an MPS configuration image.
37  *
38  * type0: 7-columns ATE image without write/read command information.
39  *
40  * type1: 8-columns ATE image that includes write/read command information,
41  *        specifying byte, word, block, or process call operations.
42  *
43  * typeUnknown: Unknown or unsupported image format.
44  */
45 enum class MPSImageType : uint8_t
46 {
47     type0 = 0,
48     type1,
49     typeUnknown = 0xFF
50 };
51 
52 enum class MPSPage : uint8_t
53 {
54     page0 = 0,
55     page1,
56     page2,
57     page3,
58     page4,
59     page29 = 0x29,
60     page2A = 0x2A,
61 };
62 
63 struct MPSData
64 {
65     uint8_t page = 0;
66     uint8_t addr = 0;
67     uint8_t length = 0;
68     std::array<uint8_t, 4> data{};
69 };
70 
71 struct MPSConfig
72 {
73     uint32_t vendorId = 0;
74     uint32_t productId = 0;
75     uint32_t configId = 0;
76     uint32_t crcUser = 0;
77     uint32_t crcMulti = 0;
78     std::vector<MPSData> registersData;
79 };
80 
81 /**
82  * @brief
83  * Utility class to iterate over lines and tokenize them by tab characters.
84  */
85 class TokenizedLines
86 {
87   public:
88     TokenizedLines() = default;
TokenizedLines(const uint8_t * d,size_t s)89     TokenizedLines(const uint8_t* d, size_t s) :
90         data(reinterpret_cast<const char*>(d), s)
91     {}
92     /**
93      * @brief Iterator over tokenized lines.
94      */
95     struct Iterator
96     {
97         using iterator_category = std::forward_iterator_tag;
98         using value_type = std::vector<std::string_view>;
99         using difference_type = std::ptrdiff_t;
100         using pointer = const value_type*;
101         using reference = const value_type&;
102 
103         Iterator() = default; // End iterator
Iteratorphosphor::software::VR::TokenizedLines::Iterator104         Iterator(std::string_view sv) : remaining(sv)
105         {
106             next();
107         }
108 
operator *phosphor::software::VR::TokenizedLines::Iterator109         reference operator*() const
110         {
111             return currentTokens;
112         }
113 
operator ->phosphor::software::VR::TokenizedLines::Iterator114         pointer operator->() const
115         {
116             return &currentTokens;
117         }
118 
operator ++phosphor::software::VR::TokenizedLines::Iterator119         Iterator& operator++()
120         {
121             next();
122             return *this;
123         }
124 
operator ++phosphor::software::VR::TokenizedLines::Iterator125         Iterator operator++(int)
126         {
127             auto result = *this;
128             ++(*this);
129             return result;
130         }
131 
operator ==(const Iterator & a,const Iterator & b)132         friend bool operator==(const Iterator& a, const Iterator& b)
133         {
134             return a.remaining.empty() && b.remaining.empty();
135         }
136 
operator !=(const Iterator & a,const Iterator & b)137         friend bool operator!=(const Iterator& a, const Iterator& b)
138         {
139             return !(a == b);
140         }
141 
142       private:
143         std::string_view remaining;
144         std::vector<std::string_view> currentTokens;
145 
nextphosphor::software::VR::TokenizedLines::Iterator146         void next()
147         {
148             currentTokens.clear();
149             if (remaining.empty())
150             {
151                 return;
152             }
153 
154             // Extract current line
155             auto newlinePos = remaining.find('\n');
156             std::string_view line = remaining.substr(0, newlinePos);
157             remaining = (newlinePos == std::string_view::npos)
158                             ? std::string_view{}
159                             : remaining.substr(newlinePos + 1);
160 
161             // Tokenize by tab
162             size_t start = 0;
163             while (start < line.size())
164             {
165                 start = line.find_first_not_of('\t', start);
166                 if (start == std::string_view::npos)
167                 {
168                     break;
169                 }
170 
171                 auto end = line.find('\t', start);
172                 currentTokens.emplace_back(line.substr(start, end - start));
173                 start = (end == std::string_view::npos) ? line.size() : end;
174             }
175         }
176     };
177 
begin() const178     Iterator begin() const
179     {
180         return Iterator(data);
181     }
182 
end()183     static Iterator end()
184     {
185         return Iterator();
186     }
187 
188   private:
189     std::string_view data;
190 };
191 
192 /**
193  * @brief Base parser for MPS configuration images.
194  */
195 class MPSImageParser
196 {
197   public:
198     MPSImageParser() = default;
199     virtual ~MPSImageParser() = default;
200     MPSImageParser(const MPSImageParser&) = delete;
201     MPSImageParser& operator=(const MPSImageParser&) = delete;
202     MPSImageParser(MPSImageParser&&) = default;
203     MPSImageParser& operator=(MPSImageParser&&) = default;
204 
205     template <typename>
206     inline static constexpr bool always_false = false;
207 
208     /**
209      * @brief Extract a typed value from a tokenized line.
210      * @tparam T Return type (string or integral type)
211      * @param tokens Tokenized line
212      * @param index Column index (ATE enum)
213      * @return Parsed value or default if invalid
214      */
215     template <typename T>
getVal(const std::vector<std::string_view> & tokens,ATE index)216     T getVal(const std::vector<std::string_view>& tokens, ATE index)
217     {
218         size_t idx = static_cast<size_t>(index);
219 
220         if (tokens.size() <= idx)
221         {
222             lg2::error("Index out of range for ATE enum: {INDEX}", "INDEX",
223                        static_cast<uint32_t>(idx));
224             return T{};
225         }
226 
227         std::string_view token = tokens[idx];
228 
229         if constexpr (std::is_same_v<T, std::string>)
230         {
231             return std::string(token);
232         }
233         else if constexpr (std::is_integral_v<T>)
234         {
235             unsigned long val = 0;
236             try
237             {
238                 val = std::stoul(std::string(token), nullptr, 16);
239             }
240             catch (...)
241             {
242                 lg2::error("Invalid hex value: {INDEX}", "INDEX",
243                            static_cast<uint32_t>(idx));
244                 return T{};
245             }
246             return static_cast<T>(val);
247         }
248         else
249         {
250             static_assert(always_false<T>, "Unsupported type in getVal");
251         }
252     }
253 
254     /**
255      * @brief Check if a tokenized line contains valid register data.
256      */
257     static bool isValidDataTokens(const std::vector<std::string_view>& tokens);
258 
259     /**
260      * @brief Parse image buffer into a list of MPSData entries.
261      */
262     virtual std::vector<MPSData> parse(
263         const uint8_t* image, size_t imageSize,
264         MPSImageType imageType = MPSImageType::typeUnknown);
265 
266     TokenizedLines lineTokens;
267 
268   private:
269     MPSData extractType0Data(const std::vector<std::string_view>& tokens);
270     MPSData extractType1Data(const std::vector<std::string_view>& tokens);
271 };
272 
273 /**
274  * @brief Base class for MPS Voltage Regulators.
275  */
276 class MPSVoltageRegulator : public VoltageRegulator
277 {
278   public:
MPSVoltageRegulator(sdbusplus::async::context & ctx,uint16_t bus,uint16_t address)279     MPSVoltageRegulator(sdbusplus::async::context& ctx, uint16_t bus,
280                         uint16_t address) :
281         VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
282     {}
283 
284     /**
285      * @brief Parse device-specific configuration from the loaded image.
286      * @return async task returning true if parsing succeeds
287      */
288     virtual sdbusplus::async::task<bool> parseDeviceConfiguration() = 0;
289 
290     /**
291      * @brief Parse an image file into internal MPS configuration.
292      * @param image Pointer to the image data
293      * @param imageSize Size of the image data
294      * @return async task returning true if parsing succeeds
295      */
296     sdbusplus::async::task<bool> parseImage(
297         const uint8_t* image, size_t imageSize,
298         MPSImageType imageType = MPSImageType::typeUnknown);
299 
300     /**
301      * @brief Group register data by page, optionally masked and shifted.
302      * @param configMask Bitmask to select relevant page bits (default 0xFF)
303      * @param shift Number of bits to shift masked value to obtain group key
304      * @return map of page keys to vector of MPSData
305      */
306     std::map<uint8_t, std::vector<MPSData>> getGroupedConfigData(
307         uint8_t configMask = 0xFF, uint8_t shift = 0);
308 
309   protected:
310     phosphor::i2c::I2C i2cInterface;
311     std::unique_ptr<MPSImageParser> parser = std::make_unique<MPSImageParser>();
312     std::unique_ptr<MPSConfig> configuration;
313 };
314 
315 } // namespace phosphor::software::VR
316