blob: 8b6d7479276231cfa508bbb4278310016d4b147e [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>
Anwaar Hadi72e584c2025-05-20 22:02:14 +000020#include <phosphor-logging/lg2.hpp>
Matt Spinlerceacf942017-10-05 13:55:02 -050021#include <xyz/openbmc_project/Common/Device/error.hpp>
Matt Spinlerf0f02b92018-10-25 16:12:43 -050022#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler015e3ad2017-08-01 11:20:47 -050023
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060024#include <filesystem>
25#include <fstream>
26
Lei YUab093322019-10-09 16:43:22 +080027namespace phosphor
Matt Spinler015e3ad2017-08-01 11:20:47 -050028{
29namespace pmbus
30{
31
32using namespace phosphor::logging;
33using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matt Spinlerceacf942017-10-05 13:55:02 -050034using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
Brandon Wyman9c7897c2019-03-28 17:42:34 -050035namespace fs = std::filesystem;
Matt Spinler015e3ad2017-08-01 11:20:47 -050036
Matt Spinlerfa23e332018-01-18 11:24:58 -060037/**
38 * @brief Helper to close a file handle
39 */
40struct FileCloser
41{
42 void operator()(FILE* fp) const
43 {
44 fclose(fp);
45 }
46};
47
Matt Spinleraf7321e2025-09-16 13:01:21 -050048/**
49 * @brief Returns the canonical path if it exists
50 * otherwise returns the path passed in.
51 *
52 * @param[in] path - The path to check
53 *
54 * @return fs::path - Either the canonical or original path
55 */
56static fs::path tryCanonical(const fs::path& path)
57{
58 std::error_code ec;
59 auto canonical = fs::canonical(path, ec);
60
61 if (ec)
62 {
63 lg2::error("Could not get canonical path from {PATH}", "PATH", path);
64 return path;
65 }
66
67 return canonical;
68}
69
Matt Spinlerf0f02b92018-10-25 16:12:43 -050070std::string PMBus::insertPageNum(const std::string& templateName, size_t page)
Matt Spinler015e3ad2017-08-01 11:20:47 -050071{
72 auto name = templateName;
73
Matt Spinlerf0f02b92018-10-25 16:12:43 -050074 // insert the page where the P was
Matt Spinler015e3ad2017-08-01 11:20:47 -050075 auto pos = name.find('P');
76 if (pos != std::string::npos)
77 {
78 name.replace(pos, 1, std::to_string(page));
79 }
80
81 return name;
82}
83
Brandon Wymanff5f3392017-08-11 17:43:22 -050084fs::path PMBus::getPath(Type type)
85{
86 switch (type)
87 {
88 default:
Brandon Wymanf855e822017-08-08 18:04:47 -050089 /* fall through */
Brandon Wymanff5f3392017-08-11 17:43:22 -050090 case Type::Base:
91 return basePath;
92 break;
93 case Type::Hwmon:
94 return basePath / "hwmon" / hwmonDir;
95 break;
96 case Type::Debug:
Matt Spinler8f0d9532017-08-21 11:22:37 -050097 return debugPath / "pmbus" / hwmonDir;
98 break;
99 case Type::DeviceDebug:
Matt Spinler4dc46782018-01-04 14:29:16 -0600100 {
Matt Spinler8f0d9532017-08-21 11:22:37 -0500101 auto dir = driverName + "." + std::to_string(instance);
102 return debugPath / dir;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500103 break;
Matt Spinler4dc46782018-01-04 14:29:16 -0600104 }
105 case Type::HwmonDeviceDebug:
106 return debugPath / "pmbus" / hwmonDir / getDeviceName();
107 break;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500108 }
109}
110
Matt Spinlerba053482018-01-04 14:26:05 -0600111std::string PMBus::getDeviceName()
112{
113 std::string name;
114 std::ifstream file;
115 auto path = basePath / "name";
116
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500117 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerba053482018-01-04 14:26:05 -0600118 std::ifstream::eofbit);
119 try
120 {
121 file.open(path);
122 file >> name;
123 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500124 catch (const std::exception& e)
Matt Spinlerba053482018-01-04 14:26:05 -0600125 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000126 lg2::error("Unable to read PMBus device name PATH={PATH}", "PATH",
127 path);
Matt Spinlerba053482018-01-04 14:26:05 -0600128 }
129
130 return name;
131}
132
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500133bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500134{
135 auto pagedBit = insertPageNum(name, page);
Matt Spinler57868bc2017-08-03 10:07:41 -0500136 return readBit(pagedBit, type);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500137}
138
Matt Spinler57868bc2017-08-03 10:07:41 -0500139bool PMBus::readBit(const std::string& name, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500140{
141 unsigned long int value = 0;
142 std::ifstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500143 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500144
Matt Spinler015e3ad2017-08-01 11:20:47 -0500145 path /= name;
146
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500147 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500148 std::ifstream::eofbit);
149
150 try
151 {
Jayanth Othayoth757ad6a2024-12-15 21:45:03 -0600152 char* err = nullptr;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000153 std::string val(1, '\0');
Matt Spinler015e3ad2017-08-01 11:20:47 -0500154
155 file.open(path);
156 file.read(&val[0], 1);
157
158 value = strtoul(val.c_str(), &err, 10);
159
160 if (*err)
161 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000162 lg2::error(
163 "Invalid character in sysfs file FILE={FILE} CONTENTS={CONTENTS}",
164 "FILE", path, "CONTENTS", val);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500165
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500166 // Catch below and handle as a read failure
Matt Spinler015e3ad2017-08-01 11:20:47 -0500167 elog<InternalFailure>();
168 }
169 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500170 catch (const std::exception& e)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500171 {
172 auto rc = errno;
173
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000174 lg2::error(
175 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
176 "ERRNO", rc, "FILENAME", path);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500177
Matt Spinlerceacf942017-10-05 13:55:02 -0500178 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
179
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500180 elog<ReadFailure>(
181 metadata::CALLOUT_ERRNO(rc),
Matt Spinleraf7321e2025-09-16 13:01:21 -0500182 metadata::CALLOUT_DEVICE_PATH(tryCanonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500183 }
184
185 return value != 0;
186}
187
Brandon Wyman3b7b38b2017-09-25 16:43:45 -0500188bool PMBus::exists(const std::string& name, Type type)
189{
190 auto path = getPath(type);
191 path /= name;
192 return fs::exists(path);
193}
194
Brandon Wyman32453e92021-12-15 19:00:14 +0000195uint64_t PMBus::read(const std::string& name, Type type, bool errTrace)
Brandon Wymanf855e822017-08-08 18:04:47 -0500196{
197 uint64_t data = 0;
198 std::ifstream file;
199 auto path = getPath(type);
200 path /= name;
201
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500202 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Brandon Wymanf855e822017-08-08 18:04:47 -0500203 std::ifstream::eofbit);
204
205 try
206 {
207 file.open(path);
208 file >> std::hex >> data;
209 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500210 catch (const std::exception& e)
Brandon Wymanf855e822017-08-08 18:04:47 -0500211 {
212 auto rc = errno;
Brandon Wymanf855e822017-08-08 18:04:47 -0500213
Brandon Wyman32453e92021-12-15 19:00:14 +0000214 if (errTrace)
215 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000216 lg2::error(
217 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
218 "ERRNO", rc, "FILENAME", path);
Brandon Wymanf855e822017-08-08 18:04:47 -0500219
Brandon Wyman32453e92021-12-15 19:00:14 +0000220 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
221
222 elog<ReadFailure>(
223 metadata::CALLOUT_ERRNO(rc),
Matt Spinleraf7321e2025-09-16 13:01:21 -0500224 metadata::CALLOUT_DEVICE_PATH(tryCanonical(basePath).c_str()));
Brandon Wyman32453e92021-12-15 19:00:14 +0000225 }
226 else
227 {
228 throw ReadFailure();
229 }
Brandon Wymanf855e822017-08-08 18:04:47 -0500230 }
231
232 return data;
233}
234
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600235std::string PMBus::readString(const std::string& name, Type type)
236{
237 std::string data;
238 std::ifstream file;
239 auto path = getPath(type);
240 path /= name;
241
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500242 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600243 std::ifstream::eofbit);
244
245 try
246 {
247 file.open(path);
248 file >> data;
249 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500250 catch (const std::exception& e)
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600251 {
252 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000253 lg2::error(
254 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
255 "ERRNO", rc, "FILENAME", path);
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600256
257 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
258
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500259 elog<ReadFailure>(
260 metadata::CALLOUT_ERRNO(rc),
Matt Spinleraf7321e2025-09-16 13:01:21 -0500261 metadata::CALLOUT_DEVICE_PATH(tryCanonical(basePath).c_str()));
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600262 }
263
264 return data;
265}
266
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500267std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
Matt Spinlerfa23e332018-01-18 11:24:58 -0600268 size_t length)
269{
270 auto path = getPath(type) / name;
271
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500272 // Use C style IO because it's easier to handle telling the difference
273 // between hitting EOF or getting an actual error.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600274 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
275
276 if (file)
277 {
278 std::vector<uint8_t> data(length, 0);
279
Patrick Williamsf5402192024-08-16 15:20:53 -0400280 auto bytes =
281 fread(data.data(), sizeof(decltype(data[0])), length, file.get());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600282
283 if (bytes != length)
284 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500285 // If hit EOF, just return the amount of data that was read.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600286 if (feof(file.get()))
287 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500288 data.erase(data.begin() + bytes, data.end());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600289 }
290 else if (ferror(file.get()))
291 {
292 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000293 lg2::error(
294 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
295 "ERRNO", rc, "FILENAME", path);
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500296 using metadata =
297 xyz::openbmc_project::Common::Device::ReadFailure;
Matt Spinlerfa23e332018-01-18 11:24:58 -0600298
299 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
300 metadata::CALLOUT_DEVICE_PATH(
Matt Spinleraf7321e2025-09-16 13:01:21 -0500301 tryCanonical(basePath).c_str()));
Matt Spinlerfa23e332018-01-18 11:24:58 -0600302 }
303 }
304 return data;
305 }
306
307 return std::vector<uint8_t>{};
308}
309
Matt Spinler57868bc2017-08-03 10:07:41 -0500310void PMBus::write(const std::string& name, int value, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500311{
312 std::ofstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500313 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500314
Matt Spinler015e3ad2017-08-01 11:20:47 -0500315 path /= name;
316
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500317 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500318 std::ofstream::eofbit);
319
320 try
321 {
322 file.open(path);
323 file << value;
324 }
325 catch (const std::exception& e)
326 {
327 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000328 lg2::error(
329 "Failed to write sysfs file errno={ERRNO} FILENAME={FILENAME}",
330 "ERRNO", rc, "FILENAME", path);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500331
Matt Spinlerceacf942017-10-05 13:55:02 -0500332 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
333
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500334 elog<WriteFailure>(
335 metadata::CALLOUT_ERRNO(rc),
Matt Spinleraf7321e2025-09-16 13:01:21 -0500336 metadata::CALLOUT_DEVICE_PATH(tryCanonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500337 }
338}
339
Brandon Wyman59a35792020-06-04 12:37:40 -0500340void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
341 Type type)
342{
343 std::ofstream file;
344 fs::path path = getPath(type);
345
346 path /= name;
347
348 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
349 std::ofstream::eofbit);
350
351 try
352 {
353 // I need to specify binary mode when I construct the ofstream
354 file.open(path, std::ios::out | std::ios_base::binary);
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000355 lg2::debug("Write data to sysfs file FILENAME={FILENAME}", "FILENAME",
356 path);
Brandon Wyman59a35792020-06-04 12:37:40 -0500357 file.write(reinterpret_cast<const char*>(&data[0]), data.size());
358 }
359 catch (const std::exception& e)
360 {
361 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000362 lg2::error(
363 "Failed to write binary data to sysfs file errno={ERRNO} FILENAME={FILENAME}",
364 "ERRNO", rc, "FILENAME", path);
Brandon Wyman59a35792020-06-04 12:37:40 -0500365
366 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
367
368 elog<WriteFailure>(
369 metadata::CALLOUT_ERRNO(rc),
Matt Spinleraf7321e2025-09-16 13:01:21 -0500370 metadata::CALLOUT_DEVICE_PATH(tryCanonical(basePath).c_str()));
Brandon Wyman59a35792020-06-04 12:37:40 -0500371 }
372}
373
Brandon Wymanff5f3392017-08-11 17:43:22 -0500374void PMBus::findHwmonDir()
Matt Spinler57868bc2017-08-03 10:07:41 -0500375{
376 fs::path path{basePath};
377 path /= "hwmon";
378
Brandon Wymanaad73e92017-08-16 16:27:54 -0500379 // Make sure the directory exists, otherwise for things that can be
380 // dynamically present or not present an exception will be thrown if the
381 // hwmon directory is not there, resulting in a program termination.
382 if (fs::is_directory(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500383 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500384 // look for <basePath>/hwmon/hwmonN/
Brandon Wymanaad73e92017-08-16 16:27:54 -0500385 for (auto& f : fs::directory_iterator(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500386 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500387 if ((f.path().filename().string().find("hwmon") !=
388 std::string::npos) &&
389 (fs::is_directory(f.path())))
390 {
391 hwmonDir = f.path().filename();
392 break;
393 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500394 }
395 }
396
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500397 // Don't really want to crash here, just log it
398 // and let accesses fail later
Brandon Wymanff5f3392017-08-11 17:43:22 -0500399 if (hwmonDir.empty())
Matt Spinler57868bc2017-08-03 10:07:41 -0500400 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000401 lg2::info("Unable to find hwmon directory in device base path "
402 "DEVICE_PATH={DEVICE_PATH}",
403 "DEVICE_PATH", basePath);
Matt Spinler57868bc2017-08-03 10:07:41 -0500404 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500405}
406
Patrick Williams92261f82025-02-01 08:22:34 -0500407std::unique_ptr<PMBusBase> PMBus::createPMBus(std::uint8_t bus,
408 const std::string& address)
Brandon Wyman8d195772020-01-27 15:03:51 -0600409{
Patrick Williamsf5402192024-08-16 15:20:53 -0400410 const std::string physpath = {
411 "/sys/bus/i2c/devices/" + std::to_string(bus) + "-" + address};
Brandon Wyman8d195772020-01-27 15:03:51 -0600412 auto interface = std::make_unique<PMBus>(physpath);
413
414 return interface;
415}
416
417std::unique_ptr<PMBusBase> createPMBus(std::uint8_t bus,
418 const std::string& address)
419{
420 return PMBus::createPMBus(bus, address);
421}
422
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500423} // namespace pmbus
Lei YUab093322019-10-09 16:43:22 +0800424} // namespace phosphor