blob: b2fe2fd1f813abd3ba309df86c5dc240d6bedd0f [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 }
101 catch (std::exception& e)
102 {
103 log<level::ERR>("Unable to read PMBus device name",
104 entry("PATH=%s", path.c_str()));
105 }
106
107 return name;
108}
109
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500110bool PMBus::readBitInPage(const std::string& name, size_t page, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500111{
112 auto pagedBit = insertPageNum(name, page);
Matt Spinler57868bc2017-08-03 10:07:41 -0500113 return readBit(pagedBit, type);
Matt Spinler015e3ad2017-08-01 11:20:47 -0500114}
115
Matt Spinler57868bc2017-08-03 10:07:41 -0500116bool PMBus::readBit(const std::string& name, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500117{
118 unsigned long int value = 0;
119 std::ifstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500120 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500121
Matt Spinler015e3ad2017-08-01 11:20:47 -0500122 path /= name;
123
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500124 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500125 std::ifstream::eofbit);
126
127 try
128 {
129 char* err = NULL;
130 std::string val{1, '\0'};
131
132 file.open(path);
133 file.read(&val[0], 1);
134
135 value = strtoul(val.c_str(), &err, 10);
136
137 if (*err)
138 {
139 log<level::ERR>("Invalid character in sysfs file",
140 entry("FILE=%s", path.c_str()),
141 entry("CONTENTS=%s", val.c_str()));
142
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500143 // Catch below and handle as a read failure
Matt Spinler015e3ad2017-08-01 11:20:47 -0500144 elog<InternalFailure>();
145 }
146 }
147 catch (std::exception& e)
148 {
149 auto rc = errno;
150
151 log<level::ERR>("Failed to read sysfs file",
Brandon Wymanf855e822017-08-08 18:04:47 -0500152 entry("FILENAME=%s", path.c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500153
Matt Spinlerceacf942017-10-05 13:55:02 -0500154 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
155
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500156 elog<ReadFailure>(
157 metadata::CALLOUT_ERRNO(rc),
158 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500159 }
160
161 return value != 0;
162}
163
Brandon Wyman3b7b38b2017-09-25 16:43:45 -0500164bool PMBus::exists(const std::string& name, Type type)
165{
166 auto path = getPath(type);
167 path /= name;
168 return fs::exists(path);
169}
170
Brandon Wymanf855e822017-08-08 18:04:47 -0500171uint64_t PMBus::read(const std::string& name, Type type)
172{
173 uint64_t data = 0;
174 std::ifstream file;
175 auto path = getPath(type);
176 path /= name;
177
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500178 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Brandon Wymanf855e822017-08-08 18:04:47 -0500179 std::ifstream::eofbit);
180
181 try
182 {
183 file.open(path);
184 file >> std::hex >> data;
185 }
186 catch (std::exception& e)
187 {
188 auto rc = errno;
189 log<level::ERR>("Failed to read sysfs file",
190 entry("FILENAME=%s", path.c_str()));
191
Matt Spinlerceacf942017-10-05 13:55:02 -0500192 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
Brandon Wymanf855e822017-08-08 18:04:47 -0500193
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500194 elog<ReadFailure>(
195 metadata::CALLOUT_ERRNO(rc),
196 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Brandon Wymanf855e822017-08-08 18:04:47 -0500197 }
198
199 return data;
200}
201
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600202std::string PMBus::readString(const std::string& name, Type type)
203{
204 std::string data;
205 std::ifstream file;
206 auto path = getPath(type);
207 path /= name;
208
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500209 file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600210 std::ifstream::eofbit);
211
212 try
213 {
214 file.open(path);
215 file >> data;
216 }
217 catch (std::exception& e)
218 {
219 auto rc = errno;
220 log<level::ERR>("Failed to read sysfs file",
221 entry("FILENAME=%s", path.c_str()));
222
223 using metadata = xyz::openbmc_project::Common::Device::ReadFailure;
224
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500225 elog<ReadFailure>(
226 metadata::CALLOUT_ERRNO(rc),
227 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinlerfbae7b62018-01-04 14:33:13 -0600228 }
229
230 return data;
231}
232
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500233std::vector<uint8_t> PMBus::readBinary(const std::string& name, Type type,
Matt Spinlerfa23e332018-01-18 11:24:58 -0600234 size_t length)
235{
236 auto path = getPath(type) / name;
237
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500238 // Use C style IO because it's easier to handle telling the difference
239 // between hitting EOF or getting an actual error.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600240 std::unique_ptr<FILE, FileCloser> file{fopen(path.c_str(), "rb")};
241
242 if (file)
243 {
244 std::vector<uint8_t> data(length, 0);
245
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500246 auto bytes =
247 fread(data.data(), sizeof(decltype(data[0])), length, file.get());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600248
249 if (bytes != length)
250 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500251 // If hit EOF, just return the amount of data that was read.
Matt Spinlerfa23e332018-01-18 11:24:58 -0600252 if (feof(file.get()))
253 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500254 data.erase(data.begin() + bytes, data.end());
Matt Spinlerfa23e332018-01-18 11:24:58 -0600255 }
256 else if (ferror(file.get()))
257 {
258 auto rc = errno;
259 log<level::ERR>("Failed to read sysfs file",
260 entry("FILENAME=%s", path.c_str()));
261
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500262 using metadata =
263 xyz::openbmc_project::Common::Device::ReadFailure;
Matt Spinlerfa23e332018-01-18 11:24:58 -0600264
265 elog<ReadFailure>(metadata::CALLOUT_ERRNO(rc),
266 metadata::CALLOUT_DEVICE_PATH(
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500267 fs::canonical(basePath).c_str()));
Matt Spinlerfa23e332018-01-18 11:24:58 -0600268 }
269 }
270 return data;
271 }
272
273 return std::vector<uint8_t>{};
274}
275
Matt Spinler57868bc2017-08-03 10:07:41 -0500276void PMBus::write(const std::string& name, int value, Type type)
Matt Spinler015e3ad2017-08-01 11:20:47 -0500277{
278 std::ofstream file;
Brandon Wymanff5f3392017-08-11 17:43:22 -0500279 fs::path path = getPath(type);
Matt Spinler57868bc2017-08-03 10:07:41 -0500280
Matt Spinler015e3ad2017-08-01 11:20:47 -0500281 path /= name;
282
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500283 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
Matt Spinler015e3ad2017-08-01 11:20:47 -0500284 std::ofstream::eofbit);
285
286 try
287 {
288 file.open(path);
289 file << value;
290 }
291 catch (const std::exception& e)
292 {
293 auto rc = errno;
294
295 log<level::ERR>("Failed to write sysfs file",
Brandon Wymanf855e822017-08-08 18:04:47 -0500296 entry("FILENAME=%s", path.c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500297
Matt Spinlerceacf942017-10-05 13:55:02 -0500298 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
299
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500300 elog<WriteFailure>(
301 metadata::CALLOUT_ERRNO(rc),
302 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
Matt Spinler015e3ad2017-08-01 11:20:47 -0500303 }
304}
305
Brandon Wyman59a35792020-06-04 12:37:40 -0500306void PMBus::writeBinary(const std::string& name, std::vector<uint8_t> data,
307 Type type)
308{
309 std::ofstream file;
310 fs::path path = getPath(type);
311
312 path /= name;
313
314 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
315 std::ofstream::eofbit);
316
317 try
318 {
319 // I need to specify binary mode when I construct the ofstream
320 file.open(path, std::ios::out | std::ios_base::binary);
321 log<level::DEBUG>("Write data to sysfs file",
322 entry("FILENAME=%s", path.c_str()));
323 file.write(reinterpret_cast<const char*>(&data[0]), data.size());
324 }
325 catch (const std::exception& e)
326 {
327 auto rc = errno;
328
329 log<level::ERR>("Failed to write binary data to sysfs file",
330 entry("FILENAME=%s", path.c_str()));
331
332 using metadata = xyz::openbmc_project::Common::Device::WriteFailure;
333
334 elog<WriteFailure>(
335 metadata::CALLOUT_ERRNO(rc),
336 metadata::CALLOUT_DEVICE_PATH(fs::canonical(basePath).c_str()));
337 }
338}
339
Brandon Wymanff5f3392017-08-11 17:43:22 -0500340void PMBus::findHwmonDir()
Matt Spinler57868bc2017-08-03 10:07:41 -0500341{
342 fs::path path{basePath};
343 path /= "hwmon";
344
Brandon Wymanaad73e92017-08-16 16:27:54 -0500345 // Make sure the directory exists, otherwise for things that can be
346 // dynamically present or not present an exception will be thrown if the
347 // hwmon directory is not there, resulting in a program termination.
348 if (fs::is_directory(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500349 {
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500350 // look for <basePath>/hwmon/hwmonN/
Brandon Wymanaad73e92017-08-16 16:27:54 -0500351 for (auto& f : fs::directory_iterator(path))
Matt Spinler57868bc2017-08-03 10:07:41 -0500352 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500353 if ((f.path().filename().string().find("hwmon") !=
354 std::string::npos) &&
355 (fs::is_directory(f.path())))
356 {
357 hwmonDir = f.path().filename();
358 break;
359 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500360 }
361 }
362
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500363 // Don't really want to crash here, just log it
364 // and let accesses fail later
Brandon Wymanff5f3392017-08-11 17:43:22 -0500365 if (hwmonDir.empty())
Matt Spinler57868bc2017-08-03 10:07:41 -0500366 {
Brandon Wymanaad73e92017-08-16 16:27:54 -0500367 log<level::INFO>("Unable to find hwmon directory "
368 "in device base path",
369 entry("DEVICE_PATH=%s", basePath.c_str()));
Matt Spinler57868bc2017-08-03 10:07:41 -0500370 }
Matt Spinler57868bc2017-08-03 10:07:41 -0500371}
372
Brandon Wyman8d195772020-01-27 15:03:51 -0600373std::unique_ptr<PMBusBase> PMBus::createPMBus(std::uint8_t bus,
374 const std::string& address)
375{
376 const std::string physpath = {"/sys/bus/i2c/devices/" +
377 std::to_string(bus) + "-" + address};
378 auto interface = std::make_unique<PMBus>(physpath);
379
380 return interface;
381}
382
383std::unique_ptr<PMBusBase> createPMBus(std::uint8_t bus,
384 const std::string& address)
385{
386 return PMBus::createPMBus(bus, address);
387}
388
Matt Spinlerf0f02b92018-10-25 16:12:43 -0500389} // namespace pmbus
Lei YUab093322019-10-09 16:43:22 +0800390} // namespace phosphor