xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mps.hpp (revision 3f2f3e6a61777c16094c94c22734659a8d84bdad)
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 &currentTokens;
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