blob: 715dcd1b18e8347b3e6d301dc2ae3f03c25ec1b7 [file] [log] [blame]
Matt Spinlerb54357f2017-08-21 14:38:54 -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 */
16#include <map>
17#include <memory>
18#include <phosphor-logging/elog.hpp>
19#include <phosphor-logging/log.hpp>
20#include <elog-errors.hpp>
21#include <xyz/openbmc_project/Sensor/Device/error.hpp>
22#include <xyz/openbmc_project/Control/Device/error.hpp>
23#include <xyz/openbmc_project/Power/Fault/error.hpp>
Matt Spinlere7e432b2017-08-21 15:01:40 -050024#include "names_values.hpp"
Matt Spinlerb54357f2017-08-21 14:38:54 -050025#include "ucd90160.hpp"
26
27namespace witherspoon
28{
29namespace power
30{
31
32using namespace std::string_literals;
33
Matt Spinlere7e432b2017-08-21 15:01:40 -050034const auto MFR_STATUS = "mfr_status"s;
Matt Spinler1e365692017-08-21 14:43:55 -050035
Matt Spinlerb54357f2017-08-21 14:38:54 -050036const auto DEVICE_NAME = "UCD90160"s;
37const auto DRIVER_NAME = "ucd9000"s;
Matt Spinlere7e432b2017-08-21 15:01:40 -050038constexpr auto NUM_PAGES = 16;
Matt Spinlerb54357f2017-08-21 14:38:54 -050039
Matt Spinler110b2842017-08-21 15:23:27 -050040namespace fs = std::experimental::filesystem;
Matt Spinlerd998b732017-08-21 15:35:54 -050041using namespace gpio;
Matt Spinlerb54357f2017-08-21 14:38:54 -050042using namespace pmbus;
43using namespace phosphor::logging;
44using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
45using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
46using namespace sdbusplus::xyz::openbmc_project::Power::Fault::Error;
47
48UCD90160::UCD90160(size_t instance) :
Matt Spinlerfcd4a712017-09-19 10:45:07 -050049 Device(DEVICE_NAME, instance),
50 interface(std::get<ucd90160::pathField>(
51 deviceMap.find(instance)->second),
52 DRIVER_NAME,
53 instance),
54 gpioDevice(findGPIODevice(interface.path()))
Matt Spinlerb54357f2017-08-21 14:38:54 -050055{
56}
57
58void UCD90160::onFailure()
59{
60 try
61 {
62 auto voutError = checkVOUTFaults();
63
64 auto pgoodError = checkPGOODFaults(false);
65
66 //Not a voltage or PGOOD fault, but we know something
67 //failed so still create an error log.
68 if (!voutError && !pgoodError)
69 {
70 createPowerFaultLog();
71 }
72 }
73 catch (ReadFailure& e)
74 {
75 if (!accessError)
76 {
77 commit<ReadFailure>();
78 accessError = true;
79 }
80 }
81}
82
83void UCD90160::analyze()
84{
85 try
86 {
87 //Note: Voltage faults are always fatal, so they just
88 //need to be analyzed in onFailure().
89
90 checkPGOODFaults(true);
91 }
92 catch (ReadFailure& e)
93 {
94 if (!accessError)
95 {
96 commit<ReadFailure>();
97 accessError = true;
98 }
99 }
100}
101
Matt Spinlere7e432b2017-08-21 15:01:40 -0500102uint16_t UCD90160::readStatusWord()
103{
104 return interface.read(STATUS_WORD, Type::Debug);
105}
106
107uint32_t UCD90160::readMFRStatus()
108{
109 return interface.read(MFR_STATUS, Type::DeviceDebug);
110}
111
Matt Spinlerb54357f2017-08-21 14:38:54 -0500112bool UCD90160::checkVOUTFaults()
113{
Matt Spinlere7e432b2017-08-21 15:01:40 -0500114 bool errorCreated = false;
115 auto statusWord = readStatusWord();
116
117 //The status_word register has a summary bit to tell us
118 //if each page even needs to be checked
119 if (!(statusWord & status_word::VOUT_FAULT))
120 {
121 return errorCreated;
122 }
123
124 for (size_t page = 0; page < NUM_PAGES; page++)
125 {
126 if (isVoutFaultLogged(page))
127 {
128 continue;
129 }
130
131 auto statusVout = interface.insertPageNum(STATUS_VOUT, page);
132 uint8_t vout = interface.read(statusVout, Type::Debug);
133
134 //Any bit on is an error
135 if (vout)
136 {
137 auto& railNames = std::get<ucd90160::railNamesField>(
138 deviceMap.find(getInstance())->second);
139 auto railName = railNames.at(page);
140
141 util::NamesValues nv;
142 nv.add("STATUS_WORD", statusWord);
143 nv.add("STATUS_VOUT", vout);
144 nv.add("MFR_STATUS", readMFRStatus());
145
146 using metadata = xyz::openbmc_project::Power::Fault::
147 PowerSequencerVoltageFault;
148
149 report<PowerSequencerVoltageFault>(
150 metadata::RAIL(page),
151 metadata::RAIL_NAME(railName.c_str()),
152 metadata::RAW_STATUS(nv.get().c_str()));
153
154 setVoutFaultLogged(page);
155 errorCreated = true;
156 }
157 }
158
159 return errorCreated;
Matt Spinlerb54357f2017-08-21 14:38:54 -0500160}
161
162bool UCD90160::checkPGOODFaults(bool polling)
163{
Matt Spinlerd998b732017-08-21 15:35:54 -0500164 bool errorCreated = false;
165
166 //While PGOOD faults could show up in MFR_STATUS (and we could then
167 //check the summary bit in STATUS_WORD first), they are edge triggered,
168 //and as the device driver sends a clear faults command every time we
169 //do a read, we will never see them. So, we'll have to just read the
170 //real time GPI status GPIO.
171
172 //Check only the GPIs configured on this system.
173 auto& gpiConfigs = std::get<ucd90160::gpiConfigField>(
174 deviceMap.find(getInstance())->second);
175
176 for (const auto& gpiConfig : gpiConfigs)
177 {
178 auto gpiNum = std::get<ucd90160::gpiNumField>(gpiConfig);
179 auto doPoll = std::get<ucd90160::pollField>(gpiConfig);
180
181 //Can skip this one if there is already an error on this input,
182 //or we are polling and these inputs don't need to be polled
183 //(because errors on them are fatal).
184 if (isPGOODFaultLogged(gpiNum) || (polling && !doPoll))
185 {
186 continue;
187 }
188
189 //The real time status is read via the pin ID
190 auto pinID = std::get<ucd90160::pinIDField>(gpiConfig);
191 auto gpio = gpios.find(pinID);
192 Value gpiStatus;
193
194 try
195 {
196 //The first time through, create the GPIO objects
197 if (gpio == gpios.end())
198 {
199 gpios.emplace(
200 pinID,
201 std::make_unique<GPIO>(
202 gpioDevice, pinID, Direction::input));
203 gpio = gpios.find(pinID);
204 }
205
206 gpiStatus = gpio->second->read();
207 }
208 catch (std::exception& e)
209 {
210 if (!accessError)
211 {
212 log<level::ERR>(e.what());
213 accessError = true;
214 }
215 continue;
216 }
217
218 if (gpiStatus == Value::low)
219 {
Matt Spinler8bc12832017-09-19 11:17:54 -0500220 //There may be some extra analysis we can do to narrow the
221 //error down further. Note that finding an error here won't
222 //prevent us from checking this GPI again.
223 errorCreated = doExtraAnalysis(gpiConfig);
224
225 if (errorCreated)
226 {
227 continue;
228 }
229
Matt Spinlerd998b732017-08-21 15:35:54 -0500230 auto& gpiName = std::get<ucd90160::gpiNameField>(gpiConfig);
231 auto status = (gpiStatus == Value::low) ? 0 : 1;
232
233 util::NamesValues nv;
234 nv.add("STATUS_WORD", readStatusWord());
235 nv.add("MFR_STATUS", readMFRStatus());
236 nv.add("INPUT_STATUS", status);
237
238 using metadata = xyz::openbmc_project::Power::Fault::
239 PowerSequencerPGOODFault;
240
241 report<PowerSequencerPGOODFault>(
242 metadata::INPUT_NUM(gpiNum),
243 metadata::INPUT_NAME(gpiName.c_str()),
244 metadata::RAW_STATUS(nv.get().c_str()));
245
246 setPGOODFaultLogged(gpiNum);
247 errorCreated = true;
248 }
249 }
250
251 return errorCreated;
Matt Spinlerb54357f2017-08-21 14:38:54 -0500252}
253
254void UCD90160::createPowerFaultLog()
255{
Matt Spinler9efb3082017-08-21 15:43:43 -0500256 util::NamesValues nv;
257 nv.add("STATUS_WORD", readStatusWord());
258 nv.add("MFR_STATUS", readMFRStatus());
Matt Spinlerb54357f2017-08-21 14:38:54 -0500259
Matt Spinler9efb3082017-08-21 15:43:43 -0500260 using metadata = xyz::openbmc_project::Power::Fault::
261 PowerSequencerFault;
262
263 report<PowerSequencerFault>(
264 metadata::RAW_STATUS(nv.get().c_str()));
Matt Spinlerb54357f2017-08-21 14:38:54 -0500265}
266
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500267fs::path UCD90160::findGPIODevice(const fs::path& path)
Matt Spinler110b2842017-08-21 15:23:27 -0500268{
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500269 fs::path gpioDevicePath;
Matt Spinler110b2842017-08-21 15:23:27 -0500270
271 //In the driver directory, look for a subdirectory
272 //named gpiochipX, where X is some number. Then
273 //we'll access the GPIO at /dev/gpiochipX.
274 if (fs::is_directory(path))
275 {
276 for (auto& f : fs::directory_iterator(path))
277 {
278 if (f.path().filename().string().find("gpiochip") !=
279 std::string::npos)
280 {
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500281 gpioDevicePath = "/dev" / f.path().filename();
Matt Spinler110b2842017-08-21 15:23:27 -0500282 break;
283 }
284 }
285 }
286
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500287 if (gpioDevicePath.empty())
Matt Spinler110b2842017-08-21 15:23:27 -0500288 {
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500289 log<level::ERR>("Could not find GPIO device path",
Matt Spinler110b2842017-08-21 15:23:27 -0500290 entry("BASE_PATH=%s", path.c_str()));
291 }
Matt Spinlerfcd4a712017-09-19 10:45:07 -0500292
293 return gpioDevicePath;
Matt Spinler110b2842017-08-21 15:23:27 -0500294}
295
Matt Spinler8bc12832017-09-19 11:17:54 -0500296bool UCD90160::doExtraAnalysis(const ucd90160::GPIConfig& config)
297{
298
299 auto type = std::get<ucd90160::extraAnalysisField>(config);
300 if (type == ucd90160::extraAnalysisType::none)
301 {
302 return false;
303 }
304
305 //Currently the only extra analysis to do is to check other GPIOs.
306 return doGPIOAnalysis(type);
307}
308
309bool UCD90160::doGPIOAnalysis(ucd90160::extraAnalysisType type)
310{
311 bool errorFound = false;
312
313 const auto& analysisConfig = std::get<ucd90160::gpioAnalysisField>(
314 deviceMap.find(getInstance())->second);
315
316 auto gpioConfig = analysisConfig.find(type);
317 if (gpioConfig == analysisConfig.end())
318 {
319 return errorFound;
320 }
321
322 auto path = std::get<ucd90160::gpioDevicePathField>(
323 gpioConfig->second);
324
325 //The /dev/gpiochipX device
326 auto device = findGPIODevice(path);
327
328 //The GPIO value of the fault condition
329 auto polarity = std::get<ucd90160::gpioPolarityField>(
330 gpioConfig->second);
331
332 //The GPIOs to check
333 auto& gpios = std::get<ucd90160::gpioDefinitionField>(
334 gpioConfig->second);
335
336 for (const auto& gpio : gpios)
337 {
338 gpio::Value value;
339
340 try
341 {
342 GPIO g{device,
343 std::get<ucd90160::gpioNumField>(gpio),
344 Direction::input};
345
346 value = g.read();
347 }
348 catch (std::exception& e)
349 {
350 if (!gpioAccessError)
351 {
352 //GPIO only throws InternalErrors - not worth committing.
353 log<level::ERR>(
354 "GPIO read failed while analyzing a power fault",
355 entry("CHIP_PATH=%s", path.c_str()));
356
357 gpioAccessError = true;
358 }
359 continue;
360 }
361
362 if (value == polarity)
363 {
364 errorFound = true;
365
366 auto part = std::get<ucd90160::gpioCalloutField>(gpio);
367 PartCallout callout{type, part};
368
369 if (isPartCalledOut(callout))
370 {
371 continue;
372 }
373
374 //Look up and call the error creation function
375 auto logError = std::get<ucd90160::errorFunctionField>(
376 gpioConfig->second);
377
378 logError(*this, part);
379
380 //Save the part callout so we don't call it out again
381 setPartCallout(callout);
382 }
383 }
384
385 return errorFound;
386}
387
Matt Spinler7b14db22017-09-19 10:57:54 -0500388void UCD90160::gpuPGOODError(const std::string& callout)
389{
390 util::NamesValues nv;
391 nv.add("STATUS_WORD", readStatusWord());
392 nv.add("MFR_STATUS", readMFRStatus());
393
394 using metadata = xyz::openbmc_project::Power::Fault::GPUPowerFault;
395
396 report<GPUPowerFault>(
397 metadata::RAW_STATUS(nv.get().c_str()),
398 metadata::GPU(callout.c_str()));
399}
400
401void UCD90160::gpuOverTempError(const std::string& callout)
402{
403 util::NamesValues nv;
404 nv.add("STATUS_WORD", readStatusWord());
405 nv.add("MFR_STATUS", readMFRStatus());
406
407 using metadata = xyz::openbmc_project::Power::Fault::GPUOverTemp;
408
409 report<GPUOverTemp>(
410 metadata::RAW_STATUS(nv.get().c_str()),
411 metadata::GPU(callout.c_str()));
412}
413
Matt Spinlerb54357f2017-08-21 14:38:54 -0500414}
415}