blob: 7bdda54194ad633222843da703cfd95d68796b1e [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 Spinlerf0f02b92018-10-25 16:12:43 -050048std::string PMBus::insertPageNum(const std::string& templateName, size_t page)
Matt Spinler015e3ad2017-08-01 11:20:47 -050049{
50 auto name = templateName;
51
Matt Spinlerf0f02b92018-10-25 16:12:43 -050052 // insert the page where the P was
Matt Spinler015e3ad2017-08-01 11:20:47 -050053 auto pos = name.find('P');
54 if (pos != std::string::npos)
55 {
56 name.replace(pos, 1, std::to_string(page));
57 }
58
59 return name;
60}
61
Brandon Wymanff5f3392017-08-11 17:43:22 -050062fs::path PMBus::getPath(Type type)
63{
64 switch (type)
65 {
66 default:
Brandon Wymanf855e822017-08-08 18:04:47 -050067 /* fall through */
Brandon Wymanff5f3392017-08-11 17:43:22 -050068 case Type::Base:
69 return basePath;
70 break;
71 case Type::Hwmon:
72 return basePath / "hwmon" / hwmonDir;
73 break;
74 case Type::Debug:
Matt Spinler8f0d9532017-08-21 11:22:37 -050075 return debugPath / "pmbus" / hwmonDir;
76 break;
77 case Type::DeviceDebug:
Matt Spinler4dc46782018-01-04 14:29:16 -060078 {
Matt Spinler8f0d9532017-08-21 11:22:37 -050079 auto dir = driverName + "." + std::to_string(instance);
80 return debugPath / dir;
Brandon Wymanff5f3392017-08-11 17:43:22 -050081 break;
Matt Spinler4dc46782018-01-04 14:29:16 -060082 }
83 case Type::HwmonDeviceDebug:
84 return debugPath / "pmbus" / hwmonDir / getDeviceName();
85 break;
Brandon Wymanff5f3392017-08-11 17:43:22 -050086 }
87}
88
Matt Spinlerba053482018-01-04 14:26:05 -060089std::string PMBus::getDeviceName()
90{
91 std::string name;
92 std::ifstream file;
93 auto path = basePath / "name";
94
Matt Spinlerf0f02b92018-10-25 16:12:43 -050095 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerba053482018-01-04 14:26:05 -060096 std::ifstream::eofbit);
97 try
98 {
99 file.open(path);
100 file >> name;
101 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500102 catch (const std::exception& e)
Matt Spinlerba053482018-01-04 14:26:05 -0600103 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000104 lg2::error("Unable to read PMBus device name PATH={PATH}", "PATH",
105 path);
Matt Spinlerba053482018-01-04 14:26:05 -0600106 }
107
108 return name;
109}
110
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500111bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500112{
113 auto pagedBit = insertPageNum(name, page);
Matt Spinler57868bc2017-08-03 10:07:41 -0500114 return readBit(pagedBit, type);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500115}
116
Matt Spinler57868bc2017-08-03 10:07:41 -0500117bool PMBus::readBit(const std::string& name, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500118{
119 unsigned long int value = 0;
120 std::ifstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500121 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500122
Matt Spinler015e3ad2017-08-01 11:20:47 -0500123 path /= name;
124
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500125 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500126 std::ifstream::eofbit);
127
128 try
129 {
Jayanth Othayoth757ad6a2024-12-15 21:45:03 -0600130 char* err = nullptr;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000131 std::string val(1, '\0');
Matt Spinler015e3ad2017-08-01 11:20:47 -0500132
133 file.open(path);
134 file.read(&val[0], 1);
135
136 value = strtoul(val.c_str(), &err, 10);
137
138 if (*err)
139 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000140 lg2::error(
141 "Invalid character in sysfs file FILE={FILE} CONTENTS={CONTENTS}",
142 "FILE", path, "CONTENTS", val);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500143
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500144 // Catch below and handle as a read failure
Matt Spinler015e3ad2017-08-01 11:20:47 -0500145 elog<InternalFailure>();
146 }
147 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500148 catch (const std::exception& e)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500149 {
150 auto rc = errno;
151
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000152 lg2::error(
153 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
154 "ERRNO", rc, "FILENAME", path);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500155
Matt Spinlerceacf942017-10-05 13:55:02 -0500156 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
157
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500158 elog<ReadFailure>(
159 metadata::CALLOUT_ERRNO(rc),
160 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500161 }
162
163 return value != 0;
164}
165
Brandon Wyman3b7b38b2017-09-25 16:43:45 -0500166bool PMBus::exists(const std::string& name, Type type)
167{
168 auto path = getPath(type);
169 path /= name;
170 return fs::exists(path);
171}
172
Brandon Wyman32453e92021-12-15 19:00:14 +0000173uint64_t PMBus::read(const std::string& name, Type type, bool errTrace)
Brandon Wymanf855e822017-08-08 18:04:47 -0500174{
175 uint64_t data = 0;
176 std::ifstream file;
177 auto path = getPath(type);
178 path /= name;
179
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500180 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Brandon Wymanf855e822017-08-08 18:04:47 -0500181 std::ifstream::eofbit);
182
183 try
184 {
185 file.open(path);
186 file >> std::hex >> data;
187 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500188 catch (const std::exception& e)
Brandon Wymanf855e822017-08-08 18:04:47 -0500189 {
190 auto rc = errno;
Brandon Wymanf855e822017-08-08 18:04:47 -0500191
Brandon Wyman32453e92021-12-15 19:00:14 +0000192 if (errTrace)
193 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000194 lg2::error(
195 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
196 "ERRNO", rc, "FILENAME", path);
Brandon Wymanf855e822017-08-08 18:04:47 -0500197
Brandon Wyman32453e92021-12-15 19:00:14 +0000198 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
199
200 elog<ReadFailure>(
201 metadata::CALLOUT_ERRNO(rc),
202 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
203 }
204 else
205 {
206 throw ReadFailure();
207 }
Brandon Wymanf855e822017-08-08 18:04:47 -0500208 }
209
210 return data;
211}
212
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600213std::string PMBus::readString(const std::string& name, Type type)
214{
215 std::string data;
216 std::ifstream file;
217 auto path = getPath(type);
218 path /= name;
219
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500220 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600221 std::ifstream::eofbit);
222
223 try
224 {
225 file.open(path);
226 file >> data;
227 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500228 catch (const std::exception& e)
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600229 {
230 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000231 lg2::error(
232 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
233 "ERRNO", rc, "FILENAME", path);
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600234
235 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
236
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500237 elog<ReadFailure>(
238 metadata::CALLOUT_ERRNO(rc),
239 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600240 }
241
242 return data;
243}
244
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500245std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
Matt Spinlerfa23e332018-01-18 11:24:58 -0600246 size_t length)
247{
248 auto path = getPath(type) / name;
249
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500250 // Use C style IO because it's easier to handle telling the difference
251 // between hitting EOF or getting an actual error.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600252 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
253
254 if (file)
255 {
256 std::vector<uint8_t> data(length, 0);
257
Patrick Williamsf5402192024-08-16 15:20:53 -0400258 auto bytes =
259 fread(data.data(), sizeof(decltype(data[0])), length, file.get());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600260
261 if (bytes != length)
262 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500263 // If hit EOF, just return the amount of data that was read.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600264 if (feof(file.get()))
265 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500266 data.erase(data.begin() + bytes, data.end());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600267 }
268 else if (ferror(file.get()))
269 {
270 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000271 lg2::error(
272 "Failed to read sysfs file errno={ERRNO} FILENAME={FILENAME}",
273 "ERRNO", rc, "FILENAME", path);
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500274 using metadata =
275 xyz::openbmc_project::Common::Device::ReadFailure;
Matt Spinlerfa23e332018-01-18 11:24:58 -0600276
277 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
278 metadata::CALLOUT_DEVICE_PATH(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500279 fs::canonical(basePath).c_str()));
Matt Spinlerfa23e332018-01-18 11:24:58 -0600280 }
281 }
282 return data;
283 }
284
285 return std::vector<uint8_t>{};
286}
287
Matt Spinler57868bc2017-08-03 10:07:41 -0500288void PMBus::write(const std::string& name, int value, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500289{
290 std::ofstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500291 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500292
Matt Spinler015e3ad2017-08-01 11:20:47 -0500293 path /= name;
294
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500295 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500296 std::ofstream::eofbit);
297
298 try
299 {
300 file.open(path);
301 file << value;
302 }
303 catch (const std::exception& e)
304 {
305 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000306 lg2::error(
307 "Failed to write sysfs file errno={ERRNO} FILENAME={FILENAME}",
308 "ERRNO", rc, "FILENAME", path);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500309
Matt Spinlerceacf942017-10-05 13:55:02 -0500310 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
311
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500312 elog<WriteFailure>(
313 metadata::CALLOUT_ERRNO(rc),
314 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500315 }
316}
317
Brandon Wyman59a35792020-06-04 12:37:40 -0500318void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
319 Type type)
320{
321 std::ofstream file;
322 fs::path path = getPath(type);
323
324 path /= name;
325
326 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
327 std::ofstream::eofbit);
328
329 try
330 {
331 // I need to specify binary mode when I construct the ofstream
332 file.open(path, std::ios::out | std::ios_base::binary);
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000333 lg2::debug("Write data to sysfs file FILENAME={FILENAME}", "FILENAME",
334 path);
Brandon Wyman59a35792020-06-04 12:37:40 -0500335 file.write(reinterpret_cast<const char*>(&data[0]), data.size());
336 }
337 catch (const std::exception& e)
338 {
339 auto rc = errno;
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000340 lg2::error(
341 "Failed to write binary data to sysfs file errno={ERRNO} FILENAME={FILENAME}",
342 "ERRNO", rc, "FILENAME", path);
Brandon Wyman59a35792020-06-04 12:37:40 -0500343
344 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
345
346 elog<WriteFailure>(
347 metadata::CALLOUT_ERRNO(rc),
348 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
349 }
350}
351
Brandon Wymanff5f3392017-08-11 17:43:22 -0500352void PMBus::findHwmonDir()
Matt Spinler57868bc2017-08-03 10:07:41 -0500353{
354 fs::path path{basePath};
355 path /= "hwmon";
356
Brandon Wymanaad73e92017-08-16 16:27:54 -0500357 // Make sure the directory exists, otherwise for things that can be
358 // dynamically present or not present an exception will be thrown if the
359 // hwmon directory is not there, resulting in a program termination.
360 if (fs::is_directory(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500361 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500362 // look for <basePath>/hwmon/hwmonN/
Brandon Wymanaad73e92017-08-16 16:27:54 -0500363 for (auto& f : fs::directory_iterator(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500364 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500365 if ((f.path().filename().string().find("hwmon") !=
366 std::string::npos) &&
367 (fs::is_directory(f.path())))
368 {
369 hwmonDir = f.path().filename();
370 break;
371 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500372 }
373 }
374
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500375 // Don't really want to crash here, just log it
376 // and let accesses fail later
Brandon Wymanff5f3392017-08-11 17:43:22 -0500377 if (hwmonDir.empty())
Matt Spinler57868bc2017-08-03 10:07:41 -0500378 {
Anwaar Hadi72e584c2025-05-20 22:02:14 +0000379 lg2::info("Unable to find hwmon directory in device base path "
380 "DEVICE_PATH={DEVICE_PATH}",
381 "DEVICE_PATH", basePath);
Matt Spinler57868bc2017-08-03 10:07:41 -0500382 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500383}
384
Patrick Williams92261f82025-02-01 08:22:34 -0500385std::unique_ptr<PMBusBase> PMBus::createPMBus(std::uint8_t bus,
386 const std::string& address)
Brandon Wyman8d195772020-01-27 15:03:51 -0600387{
Patrick Williamsf5402192024-08-16 15:20:53 -0400388 const std::string physpath = {
389 "/sys/bus/i2c/devices/" + std::to_string(bus) + "-" + address};
Brandon Wyman8d195772020-01-27 15:03:51 -0600390 auto interface = std::make_unique<PMBus>(physpath);
391
392 return interface;
393}
394
395std::unique_ptr<PMBusBase> createPMBus(std::uint8_t bus,
396 const std::string& address)
397{
398 return PMBus::createPMBus(bus, address);
399}
400
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500401} // namespace pmbus
Lei YUab093322019-10-09 16:43:22 +0800402} // namespace phosphor