1*b89395b1SShawn McCarney /**
2*b89395b1SShawn McCarney  * Copyright © 2024 IBM Corporation
3*b89395b1SShawn McCarney  *
4*b89395b1SShawn McCarney  * Licensed under the Apache License, Version 2.0 (the "License");
5*b89395b1SShawn McCarney  * you may not use this file except in compliance with the License.
6*b89395b1SShawn McCarney  * You may obtain a copy of the License at
7*b89395b1SShawn McCarney  *
8*b89395b1SShawn McCarney  *     http://www.apache.org/licenses/LICENSE-2.0
9*b89395b1SShawn McCarney  *
10*b89395b1SShawn McCarney  * Unless required by applicable law or agreed to in writing, software
11*b89395b1SShawn McCarney  * distributed under the License is distributed on an "AS IS" BASIS,
12*b89395b1SShawn McCarney  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b89395b1SShawn McCarney  * See the License for the specific language governing permissions and
14*b89395b1SShawn McCarney  * limitations under the License.
15*b89395b1SShawn McCarney  */
16*b89395b1SShawn McCarney 
17*b89395b1SShawn McCarney #include "pmbus_driver_device.hpp"
18*b89395b1SShawn McCarney 
19*b89395b1SShawn McCarney #include <ctype.h> // for tolower()
20*b89395b1SShawn McCarney 
21*b89395b1SShawn McCarney #include <algorithm>
22*b89395b1SShawn McCarney #include <exception>
23*b89395b1SShawn McCarney #include <filesystem>
24*b89395b1SShawn McCarney #include <format>
25*b89395b1SShawn McCarney #include <regex>
26*b89395b1SShawn McCarney #include <stdexcept>
27*b89395b1SShawn McCarney 
28*b89395b1SShawn McCarney namespace phosphor::power::sequencer
29*b89395b1SShawn McCarney {
30*b89395b1SShawn McCarney 
31*b89395b1SShawn McCarney using namespace pmbus;
32*b89395b1SShawn McCarney namespace fs = std::filesystem;
33*b89395b1SShawn McCarney 
getGPIOValues(Services & services)34*b89395b1SShawn McCarney std::vector<int> PMBusDriverDevice::getGPIOValues(Services& services)
35*b89395b1SShawn McCarney {
36*b89395b1SShawn McCarney     // Get lower case version of device name to use as chip label
37*b89395b1SShawn McCarney     std::string label{name};
38*b89395b1SShawn McCarney     std::transform(label.begin(), label.end(), label.begin(), ::tolower);
39*b89395b1SShawn McCarney 
40*b89395b1SShawn McCarney     // Read the GPIO values by specifying the chip label
41*b89395b1SShawn McCarney     std::vector<int> values;
42*b89395b1SShawn McCarney     try
43*b89395b1SShawn McCarney     {
44*b89395b1SShawn McCarney         values = services.getGPIOValues(label);
45*b89395b1SShawn McCarney     }
46*b89395b1SShawn McCarney     catch (const std::exception& e)
47*b89395b1SShawn McCarney     {
48*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
49*b89395b1SShawn McCarney             "Unable to read GPIO values from device {} using label {}: {}",
50*b89395b1SShawn McCarney             name, label, e.what())};
51*b89395b1SShawn McCarney     }
52*b89395b1SShawn McCarney     return values;
53*b89395b1SShawn McCarney }
54*b89395b1SShawn McCarney 
getStatusWord(uint8_t page)55*b89395b1SShawn McCarney uint16_t PMBusDriverDevice::getStatusWord(uint8_t page)
56*b89395b1SShawn McCarney {
57*b89395b1SShawn McCarney     uint16_t value{0};
58*b89395b1SShawn McCarney     try
59*b89395b1SShawn McCarney     {
60*b89395b1SShawn McCarney         std::string fileName = std::format("status{:d}", page);
61*b89395b1SShawn McCarney         value = pmbusInterface->read(fileName, Type::Debug);
62*b89395b1SShawn McCarney     }
63*b89395b1SShawn McCarney     catch (const std::exception& e)
64*b89395b1SShawn McCarney     {
65*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
66*b89395b1SShawn McCarney             "Unable to read STATUS_WORD for PAGE {:d} of device {}: {}", page,
67*b89395b1SShawn McCarney             name, e.what())};
68*b89395b1SShawn McCarney     }
69*b89395b1SShawn McCarney     return value;
70*b89395b1SShawn McCarney }
71*b89395b1SShawn McCarney 
getStatusVout(uint8_t page)72*b89395b1SShawn McCarney uint8_t PMBusDriverDevice::getStatusVout(uint8_t page)
73*b89395b1SShawn McCarney {
74*b89395b1SShawn McCarney     uint8_t value{0};
75*b89395b1SShawn McCarney     try
76*b89395b1SShawn McCarney     {
77*b89395b1SShawn McCarney         std::string fileName = std::format("status{:d}_vout", page);
78*b89395b1SShawn McCarney         value = pmbusInterface->read(fileName, Type::Debug);
79*b89395b1SShawn McCarney     }
80*b89395b1SShawn McCarney     catch (const std::exception& e)
81*b89395b1SShawn McCarney     {
82*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
83*b89395b1SShawn McCarney             "Unable to read STATUS_VOUT for PAGE {:d} of device {}: {}", page,
84*b89395b1SShawn McCarney             name, e.what())};
85*b89395b1SShawn McCarney     }
86*b89395b1SShawn McCarney     return value;
87*b89395b1SShawn McCarney }
88*b89395b1SShawn McCarney 
getReadVout(uint8_t page)89*b89395b1SShawn McCarney double PMBusDriverDevice::getReadVout(uint8_t page)
90*b89395b1SShawn McCarney {
91*b89395b1SShawn McCarney     double volts{0.0};
92*b89395b1SShawn McCarney     try
93*b89395b1SShawn McCarney     {
94*b89395b1SShawn McCarney         unsigned int fileNumber = getFileNumber(page);
95*b89395b1SShawn McCarney         std::string fileName = std::format("in{}_input", fileNumber);
96*b89395b1SShawn McCarney         std::string millivoltsStr = pmbusInterface->readString(fileName,
97*b89395b1SShawn McCarney                                                                Type::Hwmon);
98*b89395b1SShawn McCarney         unsigned long millivolts = std::stoul(millivoltsStr);
99*b89395b1SShawn McCarney         volts = millivolts / 1000.0;
100*b89395b1SShawn McCarney     }
101*b89395b1SShawn McCarney     catch (const std::exception& e)
102*b89395b1SShawn McCarney     {
103*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
104*b89395b1SShawn McCarney             "Unable to read READ_VOUT for PAGE {:d} of device {}: {}", page,
105*b89395b1SShawn McCarney             name, e.what())};
106*b89395b1SShawn McCarney     }
107*b89395b1SShawn McCarney     return volts;
108*b89395b1SShawn McCarney }
109*b89395b1SShawn McCarney 
getVoutUVFaultLimit(uint8_t page)110*b89395b1SShawn McCarney double PMBusDriverDevice::getVoutUVFaultLimit(uint8_t page)
111*b89395b1SShawn McCarney {
112*b89395b1SShawn McCarney     double volts{0.0};
113*b89395b1SShawn McCarney     try
114*b89395b1SShawn McCarney     {
115*b89395b1SShawn McCarney         unsigned int fileNumber = getFileNumber(page);
116*b89395b1SShawn McCarney         std::string fileName = std::format("in{}_lcrit", fileNumber);
117*b89395b1SShawn McCarney         std::string millivoltsStr = pmbusInterface->readString(fileName,
118*b89395b1SShawn McCarney                                                                Type::Hwmon);
119*b89395b1SShawn McCarney         unsigned long millivolts = std::stoul(millivoltsStr);
120*b89395b1SShawn McCarney         volts = millivolts / 1000.0;
121*b89395b1SShawn McCarney     }
122*b89395b1SShawn McCarney     catch (const std::exception& e)
123*b89395b1SShawn McCarney     {
124*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
125*b89395b1SShawn McCarney             "Unable to read VOUT_UV_FAULT_LIMIT for PAGE {:d} of device {}: {}",
126*b89395b1SShawn McCarney             page, name, e.what())};
127*b89395b1SShawn McCarney     }
128*b89395b1SShawn McCarney     return volts;
129*b89395b1SShawn McCarney }
130*b89395b1SShawn McCarney 
getFileNumber(uint8_t page)131*b89395b1SShawn McCarney unsigned int PMBusDriverDevice::getFileNumber(uint8_t page)
132*b89395b1SShawn McCarney {
133*b89395b1SShawn McCarney     if (pageToFileNumber.empty())
134*b89395b1SShawn McCarney     {
135*b89395b1SShawn McCarney         buildPageToFileNumberMap();
136*b89395b1SShawn McCarney     }
137*b89395b1SShawn McCarney 
138*b89395b1SShawn McCarney     auto it = pageToFileNumber.find(page);
139*b89395b1SShawn McCarney     if (it == pageToFileNumber.end())
140*b89395b1SShawn McCarney     {
141*b89395b1SShawn McCarney         throw std::runtime_error{std::format(
142*b89395b1SShawn McCarney             "Unable to find hwmon file number for PAGE {:d} of device {}", page,
143*b89395b1SShawn McCarney             name)};
144*b89395b1SShawn McCarney     }
145*b89395b1SShawn McCarney 
146*b89395b1SShawn McCarney     return it->second;
147*b89395b1SShawn McCarney }
148*b89395b1SShawn McCarney 
buildPageToFileNumberMap()149*b89395b1SShawn McCarney void PMBusDriverDevice::buildPageToFileNumberMap()
150*b89395b1SShawn McCarney {
151*b89395b1SShawn McCarney     // Clear any existing mappings
152*b89395b1SShawn McCarney     pageToFileNumber.clear();
153*b89395b1SShawn McCarney 
154*b89395b1SShawn McCarney     // Build mappings using voltage label files in hwmon directory
155*b89395b1SShawn McCarney     try
156*b89395b1SShawn McCarney     {
157*b89395b1SShawn McCarney         fs::path hwmonDir = pmbusInterface->getPath(Type::Hwmon);
158*b89395b1SShawn McCarney         if (fs::is_directory(hwmonDir))
159*b89395b1SShawn McCarney         {
160*b89395b1SShawn McCarney             // Loop through all files in hwmon directory
161*b89395b1SShawn McCarney             std::string fileName;
162*b89395b1SShawn McCarney             unsigned int fileNumber;
163*b89395b1SShawn McCarney             std::optional<uint8_t> page;
164*b89395b1SShawn McCarney             for (const auto& f : fs::directory_iterator{hwmonDir})
165*b89395b1SShawn McCarney             {
166*b89395b1SShawn McCarney                 // If this is a voltage label file
167*b89395b1SShawn McCarney                 fileName = f.path().filename().string();
168*b89395b1SShawn McCarney                 if (isLabelFile(fileName, fileNumber))
169*b89395b1SShawn McCarney                 {
170*b89395b1SShawn McCarney                     // Read PMBus PAGE number from label file contents
171*b89395b1SShawn McCarney                     page = readPageFromLabelFile(fileName);
172*b89395b1SShawn McCarney                     if (page)
173*b89395b1SShawn McCarney                     {
174*b89395b1SShawn McCarney                         // Add mapping from PAGE number to file number
175*b89395b1SShawn McCarney                         pageToFileNumber.emplace(*page, fileNumber);
176*b89395b1SShawn McCarney                     }
177*b89395b1SShawn McCarney                 }
178*b89395b1SShawn McCarney             }
179*b89395b1SShawn McCarney         }
180*b89395b1SShawn McCarney     }
181*b89395b1SShawn McCarney     catch (const std::exception& e)
182*b89395b1SShawn McCarney     {
183*b89395b1SShawn McCarney         throw std::runtime_error{
184*b89395b1SShawn McCarney             std::format("Unable to map PMBus PAGE numbers to hwmon file "
185*b89395b1SShawn McCarney                         "numbers for device {}: {}",
186*b89395b1SShawn McCarney                         name, e.what())};
187*b89395b1SShawn McCarney     }
188*b89395b1SShawn McCarney }
189*b89395b1SShawn McCarney 
isLabelFile(const std::string & fileName,unsigned int & fileNumber)190*b89395b1SShawn McCarney bool PMBusDriverDevice::isLabelFile(const std::string& fileName,
191*b89395b1SShawn McCarney                                     unsigned int& fileNumber)
192*b89395b1SShawn McCarney {
193*b89395b1SShawn McCarney     bool isLabel{false};
194*b89395b1SShawn McCarney     try
195*b89395b1SShawn McCarney     {
196*b89395b1SShawn McCarney         // Check if file name has expected pattern for voltage label file
197*b89395b1SShawn McCarney         std::regex regex{"in(\\d+)_label"};
198*b89395b1SShawn McCarney         std::smatch results;
199*b89395b1SShawn McCarney         if (std::regex_match(fileName, results, regex))
200*b89395b1SShawn McCarney         {
201*b89395b1SShawn McCarney             // Verify 2 match results: entire match and one sub-match
202*b89395b1SShawn McCarney             if (results.size() == 2)
203*b89395b1SShawn McCarney             {
204*b89395b1SShawn McCarney                 // Get sub-match that contains the file number
205*b89395b1SShawn McCarney                 std::string fileNumberStr = results.str(1);
206*b89395b1SShawn McCarney                 fileNumber = std::stoul(fileNumberStr);
207*b89395b1SShawn McCarney                 isLabel = true;
208*b89395b1SShawn McCarney             }
209*b89395b1SShawn McCarney         }
210*b89395b1SShawn McCarney     }
211*b89395b1SShawn McCarney     catch (...)
212*b89395b1SShawn McCarney     {
213*b89395b1SShawn McCarney         // Ignore error.  If this file is needed for pgood fault detection, an
214*b89395b1SShawn McCarney         // error will occur later when the necessary mapping is missing.  Avoid
215*b89395b1SShawn McCarney         // logging unnecessary errors for files that may not be required.
216*b89395b1SShawn McCarney     }
217*b89395b1SShawn McCarney     return isLabel;
218*b89395b1SShawn McCarney }
219*b89395b1SShawn McCarney 
220*b89395b1SShawn McCarney std::optional<uint8_t>
readPageFromLabelFile(const std::string & fileName)221*b89395b1SShawn McCarney     PMBusDriverDevice::readPageFromLabelFile(const std::string& fileName)
222*b89395b1SShawn McCarney {
223*b89395b1SShawn McCarney     std::optional<uint8_t> page;
224*b89395b1SShawn McCarney     try
225*b89395b1SShawn McCarney     {
226*b89395b1SShawn McCarney         // Read voltage label file contents
227*b89395b1SShawn McCarney         std::string contents = pmbusInterface->readString(fileName,
228*b89395b1SShawn McCarney                                                           Type::Hwmon);
229*b89395b1SShawn McCarney 
230*b89395b1SShawn McCarney         // Check if file contents match the expected pattern
231*b89395b1SShawn McCarney         std::regex regex{"vout(\\d+)"};
232*b89395b1SShawn McCarney         std::smatch results;
233*b89395b1SShawn McCarney         if (std::regex_match(contents, results, regex))
234*b89395b1SShawn McCarney         {
235*b89395b1SShawn McCarney             // Verify 2 match results: entire match and one sub-match
236*b89395b1SShawn McCarney             if (results.size() == 2)
237*b89395b1SShawn McCarney             {
238*b89395b1SShawn McCarney                 // Get sub-match that contains the page number + 1
239*b89395b1SShawn McCarney                 std::string pageStr = results.str(1);
240*b89395b1SShawn McCarney                 page = std::stoul(pageStr) - 1;
241*b89395b1SShawn McCarney             }
242*b89395b1SShawn McCarney         }
243*b89395b1SShawn McCarney     }
244*b89395b1SShawn McCarney     catch (...)
245*b89395b1SShawn McCarney     {
246*b89395b1SShawn McCarney         // Ignore error.  If this file is needed for pgood fault detection, an
247*b89395b1SShawn McCarney         // error will occur later when the necessary mapping is missing.  Avoid
248*b89395b1SShawn McCarney         // logging unnecessary errors for files that may not be required.
249*b89395b1SShawn McCarney     }
250*b89395b1SShawn McCarney     return page;
251*b89395b1SShawn McCarney }
252*b89395b1SShawn McCarney 
253*b89395b1SShawn McCarney } // namespace phosphor::power::sequencer
254