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