xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mps.cpp (revision c1b36628298d7799677680e70d455f85acf83650)
1 #include "mps.hpp"
2 
3 namespace phosphor::software::VR
4 {
5 
6 bool MPSImageParser::isValidDataTokens(
7     const std::vector<std::string_view>& tokens)
8 {
9     return tokens.size() > static_cast<size_t>(ATE::regName) &&
10            !tokens[0].starts_with('*');
11 }
12 
13 MPSData MPSImageParser::extractType0Data(
14     const std::vector<std::string_view>& tokens)
15 {
16     MPSData data;
17     static constexpr size_t type0TokensSize = 7;
18 
19     if (tokens.size() != type0TokensSize)
20     {
21         lg2::error("Invalid token count for Type0 image line: "
22                    "expected {EXPECTED}, got {ACTUAL}",
23                    "EXPECTED", type0TokensSize, "ACTUAL", tokens.size());
24         return data;
25     }
26 
27     data.page = getVal<uint8_t>(tokens, ATE::pageNum);
28     data.addr = getVal<uint8_t>(tokens, ATE::regAddrHex);
29 
30     std::string regData = getVal<std::string>(tokens, ATE::regDataHex);
31     size_t byteCount = std::min(regData.length() / 2, size_t(4));
32     for (size_t i = 0; i < byteCount; ++i)
33     {
34         data.data[byteCount - 1 - i] = static_cast<uint8_t>(
35             std::stoul(regData.substr(i * 2, 2), nullptr, 16));
36     }
37 
38     data.length = static_cast<uint8_t>(byteCount);
39     return data;
40 }
41 
42 MPSData MPSImageParser::extractType1Data(
43     const std::vector<std::string_view>& tokens)
44 {
45     MPSData data;
46     static constexpr size_t type0TokensSize = 8;
47 
48     if (tokens.size() != type0TokensSize)
49     {
50         lg2::error("Invalid token count for Type1 image line: "
51                    "expected {EXPECTED}, got {ACTUAL}",
52                    "EXPECTED", type0TokensSize, "ACTUAL", tokens.size());
53         return data;
54     }
55 
56     data.page = getVal<uint8_t>(tokens, ATE::pageNum);
57     auto addr = getVal<uint16_t>(tokens, ATE::regAddrHex);
58     auto cmdType = getVal<std::string>(tokens, ATE::writeType);
59     int blockDataBytes = 0;
60 
61     if (cmdType.starts_with("P"))
62     {
63         // Check if these tokens represent a P1 or P2 process call.
64         // The upper byte of 'addr' is the command code, and the lower byte
65         // is the LSB of the data.
66         // Example:
67         // addr = 0x0F11 and data = 0x18, this sends 0x1811 to command 0x0F
68         static constexpr uint16_t processCallAddrMask = 0xFF00;
69         data.data[0] = static_cast<uint8_t>(addr & ~processCallAddrMask);
70         data.data[1] = getVal<uint8_t>(tokens, ATE::regDataHex);
71         data.addr = static_cast<uint8_t>((addr & processCallAddrMask) >> 8);
72         data.length = 2;
73         return data;
74     }
75     else if (cmdType.starts_with("B"))
76     {
77         // Command types starting with 'B' indicate block r/w commands.
78         // The number following 'B' specifies the number of data bytes.
79         if (cmdType.size() > 1 && std::isdigit(cmdType[1]))
80         {
81             blockDataBytes = std::stoi(cmdType.substr(1));
82         }
83     }
84 
85     std::string regData = getVal<std::string>(tokens, ATE::regDataHex);
86     size_t byteCount = std::min(regData.length() / 2, size_t(4));
87     size_t dataIndex = 0;
88 
89     if (blockDataBytes > 0)
90     {
91         data.data[dataIndex++] = static_cast<uint8_t>(blockDataBytes);
92     }
93 
94     for (size_t i = 0; i < byteCount; ++i)
95     {
96         data.data[dataIndex + (byteCount - 1 - i)] = static_cast<uint8_t>(
97             std::stoul(regData.substr(i * 2, 2), nullptr, 16));
98     }
99     data.length = static_cast<uint8_t>(dataIndex + byteCount);
100     data.addr = getVal<uint8_t>(tokens, ATE::regAddrHex);
101     return data;
102 }
103 
104 std::vector<MPSData> MPSImageParser::parse(
105     const uint8_t* image, size_t imageSize, MPSImageType imageType)
106 {
107     lineTokens = TokenizedLines(image, imageSize);
108     std::vector<MPSData> results;
109 
110     using ExtractDataFunc =
111         std::function<MPSData(const std::vector<std::string_view>&)>;
112     ExtractDataFunc extractDataFunc;
113 
114     switch (imageType)
115     {
116         case MPSImageType::type0:
117             extractDataFunc = [this](const auto& tokens) {
118                 return extractType0Data(tokens);
119             };
120             break;
121         case MPSImageType::type1:
122             extractDataFunc = [this](const auto& tokens) {
123                 return extractType1Data(tokens);
124             };
125             break;
126         default:
127             lg2::error("Unsupported or unknown MPS image type: {TYPE}", "TYPE",
128                        static_cast<int>(imageType));
129             return results;
130     }
131 
132     for (const auto& tokens : lineTokens)
133     {
134         if (tokens[0].starts_with("END"))
135         {
136             break;
137         }
138 
139         if (isValidDataTokens(tokens))
140         {
141             auto data = extractDataFunc(tokens);
142             if (data.length == 0)
143             {
144                 return {};
145             }
146             results.push_back(data);
147         }
148     }
149 
150     return results;
151 }
152 
153 sdbusplus::async::task<bool> MPSVoltageRegulator::parseImage(
154     const uint8_t* image, size_t imageSize, MPSImageType imageType)
155 {
156     configuration = std::make_unique<MPSConfig>();
157 
158     try
159     {
160         configuration->registersData =
161             parser->parse(image, imageSize, imageType);
162 
163         if (!co_await parseDeviceConfiguration())
164         {
165             co_return false;
166         }
167     }
168     catch (const std::exception& e)
169     {
170         lg2::error("Failed to parse MPS image: {ERR}", "ERR", e.what());
171         co_return false;
172     }
173 
174     lg2::debug(
175         "Parsed configuration: Data Size={SIZE}, Vendor ID={VID}, "
176         "Product ID={PID}, Config ID={CID}, CRC User={CRCUSR}, "
177         "CRC Multi={CRCMULTI}",
178         "SIZE", configuration->registersData.size(), "VID", lg2::hex,
179         configuration->vendorId, "PID", lg2::hex, configuration->productId,
180         "CID", lg2::hex, configuration->configId, "CRCUSR", lg2::hex,
181         configuration->crcUser, "CRCMULTI", lg2::hex, configuration->crcMulti);
182 
183     co_return true;
184 }
185 
186 std::map<uint8_t, std::vector<MPSData>>
187     MPSVoltageRegulator::getGroupedConfigData(uint8_t configMask, uint8_t shift)
188 {
189     std::map<uint8_t, std::vector<MPSData>> groupedData;
190 
191     if (!configuration)
192     {
193         return groupedData;
194     }
195 
196     for (const auto& data : configuration->registersData)
197     {
198         uint8_t config = (data.page & configMask) >> shift;
199         groupedData[config].push_back(data);
200     }
201 
202     return groupedData;
203 }
204 
205 } // namespace phosphor::software::VR
206