blob: 5bfbfb337196870f67da95e0e4709e3b63f780ed [file] [log] [blame]
Shawn McCarney472101c2024-04-17 16:31:09 -05001/**
2 * Copyright © 2024 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
17#include "standard_device.hpp"
18
19#include "format_utils.hpp"
20
21#include <exception>
22#include <format>
23#include <span>
24#include <stdexcept>
25
26namespace phosphor::power::sequencer
27{
28
29std::string StandardDevice::findPgoodFault(
30 Services& services, const std::string& powerSupplyError,
31 std::map<std::string, std::string>& additionalData)
32{
33 std::string error{};
34 try
35 {
36 prepareForPgoodFaultDetection(services);
37
38 // Get all GPIO values (if possible) from device. They may be slow to
39 // obtain, so obtain them once and then pass values to each Rail object.
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050040 std::vector<int> gpioValues = getGPIOValuesIfPossible(services);
Shawn McCarney472101c2024-04-17 16:31:09 -050041
Shawn McCarney16275832024-06-27 10:14:11 -050042 // Try to find a voltage rail where a pgood fault occurred
43 Rail* rail = findRailWithPgoodFault(services, gpioValues,
44 additionalData);
45 if (rail != nullptr)
Shawn McCarney472101c2024-04-17 16:31:09 -050046 {
Shawn McCarney16275832024-06-27 10:14:11 -050047 services.logErrorMsg(std::format(
48 "Pgood fault found in rail monitored by device {}", name));
49
50 // If this is a PSU rail and a PSU error was previously detected
51 if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
Shawn McCarney472101c2024-04-17 16:31:09 -050052 {
Shawn McCarney16275832024-06-27 10:14:11 -050053 // Return power supply error as root cause
54 error = powerSupplyError;
Shawn McCarney472101c2024-04-17 16:31:09 -050055 }
Shawn McCarney16275832024-06-27 10:14:11 -050056 else
57 {
58 // Return pgood fault as root cause
59 error =
60 "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
61 }
62
63 storePgoodFaultDebugData(services, gpioValues, additionalData);
Shawn McCarney472101c2024-04-17 16:31:09 -050064 }
65 }
66 catch (const std::exception& e)
67 {
68 throw std::runtime_error{std::format(
69 "Unable to determine if a pgood fault occurred in device {}: {}",
70 name, e.what())};
71 }
72 return error;
73}
74
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050075std::vector<int> StandardDevice::getGPIOValuesIfPossible(Services& services)
Shawn McCarney472101c2024-04-17 16:31:09 -050076{
77 std::vector<int> values{};
78 try
79 {
Shawn McCarneyfc3f31f2024-04-23 17:02:44 -050080 values = getGPIOValues(services);
Shawn McCarney472101c2024-04-17 16:31:09 -050081 }
82 catch (...)
83 {}
84 return values;
85}
86
Shawn McCarney16275832024-06-27 10:14:11 -050087Rail* StandardDevice::findRailWithPgoodFault(
88 Services& services, const std::vector<int>& gpioValues,
89 std::map<std::string, std::string>& additionalData)
90{
91 // Look for the first rail in the power on sequence with a pgood fault based
92 // on STATUS_VOUT. This is usually the most accurate method. For example,
93 // if a pgood fault occurs, the power sequencer device may automatically
94 // shut off related rails. Ideally the device will only set fault bits in
95 // STATUS_VOUT for the rail with the pgood fault. However, all the related
96 // rails will likely appear to be faulted by the other methods.
97 for (std::unique_ptr<Rail>& rail : rails)
98 {
99 if (rail->hasPgoodFaultStatusVout(*this, services, additionalData))
100 {
101 return rail.get();
102 }
103 }
104
105 // Look for the first rail in the power on sequence with a pgood fault based
106 // on either a GPIO or the output voltage. Both methods check if the rail
107 // is powered off. If a pgood fault occurs during the power on sequence,
108 // the power sequencer device may stop powering on rails. As a result, all
109 // rails after the faulted one in the sequence may also be powered off.
110 for (std::unique_ptr<Rail>& rail : rails)
111 {
112 if (rail->hasPgoodFaultGPIO(*this, services, gpioValues,
113 additionalData) ||
114 rail->hasPgoodFaultOutputVoltage(*this, services, additionalData))
115 {
116 return rail.get();
117 }
118 }
119
120 // No rail with pgood fault found
121 return nullptr;
122}
123
Shawn McCarney472101c2024-04-17 16:31:09 -0500124void StandardDevice::storePgoodFaultDebugData(
125 Services& services, const std::vector<int>& gpioValues,
126 std::map<std::string, std::string>& additionalData)
127{
Shawn McCarneyfe78c172024-05-02 14:01:46 -0500128 try
Shawn McCarney472101c2024-04-17 16:31:09 -0500129 {
Shawn McCarneyfe78c172024-05-02 14:01:46 -0500130 additionalData.emplace("DEVICE_NAME", name);
131 storeGPIOValues(services, gpioValues, additionalData);
132 }
133 catch (...)
134 {}
135}
136
137void StandardDevice::storeGPIOValues(
138 Services& services, const std::vector<int>& values,
139 std::map<std::string, std::string>& additionalData)
140{
141 if (!values.empty())
142 {
143 std::string valuesStr = format_utils::toString(std::span(values));
Shawn McCarney472101c2024-04-17 16:31:09 -0500144 services.logInfoMsg(
145 std::format("Device {} GPIO values: {}", name, valuesStr));
146 additionalData.emplace("GPIO_VALUES", valuesStr);
147 }
148}
149
150} // namespace phosphor::power::sequencer