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