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