blob: 9424ee6a649f110d93ee2f88070b7b7c9328fc27 [file] [log] [blame]
Matt Spinler015e3ad2017-08-01 11:20:47 -05001/**
2 * Copyright © 2017 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Matt Spinlerf0f02b92018-10-25 16:12:43 -050016#include "pmbus.hpp"
17
Matt Spinler015e3ad2017-08-01 11:20:47 -050018#include <phosphor-logging/elog-errors.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050019#include <phosphor-logging/elog.hpp>
Matt Spinlerceacf942017-10-05 13:55:02 -050020#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050021#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler015e3ad2017-08-01 11:20:47 -050022
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060023#include <filesystem>
24#include <fstream>
25
Lei YUab093322019-10-09 16:43:22 +080026namespace phosphor
Matt Spinler015e3ad2017-08-01 11:20:47 -050027{
28namespace pmbus
29{
30
31using namespace phosphor::logging;
32using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matt Spinlerceacf942017-10-05 13:55:02 -050033using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wyman9c7897c2019-03-28 17:42:34 -050034namespace fs = std::filesystem;
Matt Spinler015e3ad2017-08-01 11:20:47 -050035
Matt Spinlerfa23e332018-01-18 11:24:58 -060036/**
37 * @brief Helper to close a file handle
38 */
39struct FileCloser
40{
41 void operator()(FILE* fp) const
42 {
43 fclose(fp);
44 }
45};
46
Matt Spinlerf0f02b92018-10-25 16:12:43 -050047std::string PMBus::insertPageNum(const std::string& templateName, size_t page)
Matt Spinler015e3ad2017-08-01 11:20:47 -050048{
49 auto name = templateName;
50
Matt Spinlerf0f02b92018-10-25 16:12:43 -050051 // insert the page where the P was
Matt Spinler015e3ad2017-08-01 11:20:47 -050052 auto pos = name.find('P');
53 if (pos != std::string::npos)
54 {
55 name.replace(pos, 1, std::to_string(page));
56 }
57
58 return name;
59}
60
Brandon Wymanff5f3392017-08-11 17:43:22 -050061fs::path PMBus::getPath(Type type)
62{
63 switch (type)
64 {
65 default:
Brandon Wymanf855e822017-08-08 18:04:47 -050066 /* fall through */
Brandon Wymanff5f3392017-08-11 17:43:22 -050067 case Type::Base:
68 return basePath;
69 break;
70 case Type::Hwmon:
71 return basePath / "hwmon" / hwmonDir;
72 break;
73 case Type::Debug:
Matt Spinler8f0d9532017-08-21 11:22:37 -050074 return debugPath / "pmbus" / hwmonDir;
75 break;
76 case Type::DeviceDebug:
Matt Spinler4dc46782018-01-04 14:29:16 -060077 {
Matt Spinler8f0d9532017-08-21 11:22:37 -050078 auto dir = driverName + "." + std::to_string(instance);
79 return debugPath / dir;
Brandon Wymanff5f3392017-08-11 17:43:22 -050080 break;
Matt Spinler4dc46782018-01-04 14:29:16 -060081 }
82 case Type::HwmonDeviceDebug:
83 return debugPath / "pmbus" / hwmonDir / getDeviceName();
84 break;
Brandon Wymanff5f3392017-08-11 17:43:22 -050085 }
86}
87
Matt Spinlerba053482018-01-04 14:26:05 -060088std::string PMBus::getDeviceName()
89{
90 std::string name;
91 std::ifstream file;
92 auto path = basePath / "name";
93
Matt Spinlerf0f02b92018-10-25 16:12:43 -050094 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerba053482018-01-04 14:26:05 -060095 std::ifstream::eofbit);
96 try
97 {
98 file.open(path);
99 file >> name;
100 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500101 catch (const std::exception& e)
Matt Spinlerba053482018-01-04 14:26:05 -0600102 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500103 log<level::ERR>((std::string("Unable to read PMBus device name "
104 "PATH=") +
105 path.string())
106 .c_str());
Matt Spinlerba053482018-01-04 14:26:05 -0600107 }
108
109 return name;
110}
111
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500112bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500113{
114 auto pagedBit = insertPageNum(name, page);
Matt Spinler57868bc2017-08-03 10:07:41 -0500115 return readBit(pagedBit, type);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500116}
117
Matt Spinler57868bc2017-08-03 10:07:41 -0500118bool PMBus::readBit(const std::string& name, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500119{
120 unsigned long int value = 0;
121 std::ifstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500122 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500123
Matt Spinler015e3ad2017-08-01 11:20:47 -0500124 path /= name;
125
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500126 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500127 std::ifstream::eofbit);
128
129 try
130 {
131 char* err = NULL;
132 std::string val{1, '\0'};
133
134 file.open(path);
135 file.read(&val[0], 1);
136
137 value = strtoul(val.c_str(), &err, 10);
138
139 if (*err)
140 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500141 log<level::ERR>((std::string("Invalid character in sysfs file"
142 " FILE=") +
143 path.string() + std::string(" CONTENTS=") + val)
144 .c_str());
Matt Spinler015e3ad2017-08-01 11:20:47 -0500145
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500146 // Catch below and handle as a read failure
Matt Spinler015e3ad2017-08-01 11:20:47 -0500147 elog<InternalFailure>();
148 }
149 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500150 catch (const std::exception& e)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500151 {
152 auto rc = errno;
153
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500154 log<level::ERR>((std::string("Failed to read sysfs file "
155 "errno=") +
156 std::to_string(rc) + std::string(" FILENAME=") +
157 path.string())
158 .c_str());
Matt Spinler015e3ad2017-08-01 11:20:47 -0500159
Matt Spinlerceacf942017-10-05 13:55:02 -0500160 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
161
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500162 elog<ReadFailure>(
163 metadata::CALLOUT_ERRNO(rc),
164 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500165 }
166
167 return value != 0;
168}
169
Brandon Wyman3b7b38b2017-09-25 16:43:45 -0500170bool PMBus::exists(const std::string& name, Type type)
171{
172 auto path = getPath(type);
173 path /= name;
174 return fs::exists(path);
175}
176
Brandon Wyman32453e92021-12-15 19:00:14 +0000177uint64_t PMBus::read(const std::string& name, Type type, bool errTrace)
Brandon Wymanf855e822017-08-08 18:04:47 -0500178{
179 uint64_t data = 0;
180 std::ifstream file;
181 auto path = getPath(type);
182 path /= name;
183
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500184 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Brandon Wymanf855e822017-08-08 18:04:47 -0500185 std::ifstream::eofbit);
186
187 try
188 {
189 file.open(path);
190 file >> std::hex >> data;
191 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500192 catch (const std::exception& e)
Brandon Wymanf855e822017-08-08 18:04:47 -0500193 {
194 auto rc = errno;
Brandon Wymanf855e822017-08-08 18:04:47 -0500195
Brandon Wyman32453e92021-12-15 19:00:14 +0000196 if (errTrace)
197 {
198 log<level::ERR>((std::string("Failed to read sysfs file "
199 "errno=") +
200 std::to_string(rc) + " FILENAME=" + path.string())
201 .c_str());
Brandon Wymanf855e822017-08-08 18:04:47 -0500202
Brandon Wyman32453e92021-12-15 19:00:14 +0000203 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
204
205 elog<ReadFailure>(
206 metadata::CALLOUT_ERRNO(rc),
207 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
208 }
209 else
210 {
211 throw ReadFailure();
212 }
Brandon Wymanf855e822017-08-08 18:04:47 -0500213 }
214
215 return data;
216}
217
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600218std::string PMBus::readString(const std::string& name, Type type)
219{
220 std::string data;
221 std::ifstream file;
222 auto path = getPath(type);
223 path /= name;
224
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500225 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600226 std::ifstream::eofbit);
227
228 try
229 {
230 file.open(path);
231 file >> data;
232 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500233 catch (const std::exception& e)
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600234 {
235 auto rc = errno;
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500236 log<level::ERR>((std::string("Failed to read sysfs file "
237 "errno=") +
238 std::to_string(rc) + " FILENAME=" + path.string())
239 .c_str());
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600240
241 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
242
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500243 elog<ReadFailure>(
244 metadata::CALLOUT_ERRNO(rc),
245 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600246 }
247
248 return data;
249}
250
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500251std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
Matt Spinlerfa23e332018-01-18 11:24:58 -0600252 size_t length)
253{
254 auto path = getPath(type) / name;
255
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500256 // Use C style IO because it's easier to handle telling the difference
257 // between hitting EOF or getting an actual error.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600258 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
259
260 if (file)
261 {
262 std::vector<uint8_t> data(length, 0);
263
Patrick Williams48781ae2023-05-10 07:50:50 -0500264 auto bytes = fread(data.data(), sizeof(decltype(data[0])), length,
265 file.get());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600266
267 if (bytes != length)
268 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500269 // If hit EOF, just return the amount of data that was read.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600270 if (feof(file.get()))
271 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500272 data.erase(data.begin() + bytes, data.end());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600273 }
274 else if (ferror(file.get()))
275 {
276 auto rc = errno;
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500277 log<level::ERR>((std::string("Failed to read sysfs file "
278 "errno=") +
279 std::to_string(rc) +
280 " FILENAME=" + path.string())
281 .c_str());
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500282 using metadata =
283 xyz::openbmc_project::Common::Device::ReadFailure;
Matt Spinlerfa23e332018-01-18 11:24:58 -0600284
285 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
286 metadata::CALLOUT_DEVICE_PATH(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500287 fs::canonical(basePath).c_str()));
Matt Spinlerfa23e332018-01-18 11:24:58 -0600288 }
289 }
290 return data;
291 }
292
293 return std::vector<uint8_t>{};
294}
295
Matt Spinler57868bc2017-08-03 10:07:41 -0500296void PMBus::write(const std::string& name, int value, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500297{
298 std::ofstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500299 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500300
Matt Spinler015e3ad2017-08-01 11:20:47 -0500301 path /= name;
302
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500303 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500304 std::ofstream::eofbit);
305
306 try
307 {
308 file.open(path);
309 file << value;
310 }
311 catch (const std::exception& e)
312 {
313 auto rc = errno;
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500314 log<level::ERR>((std::string("Failed to write sysfs file "
315 "errno=") +
316 std::to_string(rc) + " FILENAME=" + path.string())
317 .c_str());
Matt Spinler015e3ad2017-08-01 11:20:47 -0500318
Matt Spinlerceacf942017-10-05 13:55:02 -0500319 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
320
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500321 elog<WriteFailure>(
322 metadata::CALLOUT_ERRNO(rc),
323 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500324 }
325}
326
Brandon Wyman59a35792020-06-04 12:37:40 -0500327void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
328 Type type)
329{
330 std::ofstream file;
331 fs::path path = getPath(type);
332
333 path /= name;
334
335 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
336 std::ofstream::eofbit);
337
338 try
339 {
340 // I need to specify binary mode when I construct the ofstream
341 file.open(path, std::ios::out | std::ios_base::binary);
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500342 log<level::DEBUG>(std::string("Write data to sysfs file "
343 "FILENAME=" +
344 path.string())
345 .c_str());
Brandon Wyman59a35792020-06-04 12:37:40 -0500346 file.write(reinterpret_cast<const char*>(&data[0]), data.size());
347 }
348 catch (const std::exception& e)
349 {
350 auto rc = errno;
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500351 log<level::ERR>(
352 (std::string("Failed to write binary data to sysfs file "
353 "errno=") +
354 std::to_string(rc) + " FILENAME=" + path.string())
355 .c_str());
Brandon Wyman59a35792020-06-04 12:37:40 -0500356
357 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
358
359 elog<WriteFailure>(
360 metadata::CALLOUT_ERRNO(rc),
361 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
362 }
363}
364
Brandon Wymanff5f3392017-08-11 17:43:22 -0500365void PMBus::findHwmonDir()
Matt Spinler57868bc2017-08-03 10:07:41 -0500366{
367 fs::path path{basePath};
368 path /= "hwmon";
369
Brandon Wymanaad73e92017-08-16 16:27:54 -0500370 // Make sure the directory exists, otherwise for things that can be
371 // dynamically present or not present an exception will be thrown if the
372 // hwmon directory is not there, resulting in a program termination.
373 if (fs::is_directory(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500374 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500375 // look for <basePath>/hwmon/hwmonN/
Brandon Wymanaad73e92017-08-16 16:27:54 -0500376 for (auto& f : fs::directory_iterator(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500377 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500378 if ((f.path().filename().string().find("hwmon") !=
379 std::string::npos) &&
380 (fs::is_directory(f.path())))
381 {
382 hwmonDir = f.path().filename();
383 break;
384 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500385 }
386 }
387
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500388 // Don't really want to crash here, just log it
389 // and let accesses fail later
Brandon Wymanff5f3392017-08-11 17:43:22 -0500390 if (hwmonDir.empty())
Matt Spinler57868bc2017-08-03 10:07:41 -0500391 {
Jay Meyer6a3fd2c2020-08-25 16:37:16 -0500392 log<level::INFO>(std::string("Unable to find hwmon directory "
393 "in device base path"
394 " DEVICE_PATH=" +
395 basePath.string())
396 .c_str());
Matt Spinler57868bc2017-08-03 10:07:41 -0500397 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500398}
399
Brandon Wyman8d195772020-01-27 15:03:51 -0600400std::unique_ptr<PMBusBase> PMBus::createPMBus(std::uint8_t bus,
401 const std::string& address)
402{
403 const std::string physpath = {"/sys/bus/i2c/devices/" +
404 std::to_string(bus) + "-" + address};
405 auto interface = std::make_unique<PMBus>(physpath);
406
407 return interface;
408}
409
410std::unique_ptr<PMBusBase> createPMBus(std::uint8_t bus,
411 const std::string& address)
412{
413 return PMBus::createPMBus(bus, address);
414}
415
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500416} // namespace pmbus
Lei YUab093322019-10-09 16:43:22 +0800417} // namespace phosphor