xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/chassis.cpp (revision b533016873041ca9287409d68f06c873ebeb0f24)
1 /**
2  * Copyright © 2025 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 "chassis.hpp"
18 
19 namespace phosphor::power::sequencer
20 {
21 
canSetPowerState(PowerState newPowerState)22 std::tuple<bool, std::string> Chassis::canSetPowerState(
23     PowerState newPowerState)
24 {
25     verifyMonitoringInitialized();
26     try
27     {
28         if (powerState && (powerState == newPowerState))
29         {
30             return {false, "Chassis is already at requested state"};
31         }
32 
33         if (!isPresent())
34         {
35             return {false, "Chassis is not present"};
36         }
37 
38         // Do not allow power on for chassis in hardware isolation; power off OK
39         if (!isEnabled() && (newPowerState == PowerState::on))
40         {
41             return {false, "Chassis is not enabled"};
42         }
43 
44         if (!isInputPowerGood())
45         {
46             return {false, "Chassis does not have input power"};
47         }
48 
49         // Check Available last. This D-Bus property is based on a list of
50         // factors including some of the preceding properties.
51         if (!isAvailable())
52         {
53             return {false, "Chassis is not available"};
54         }
55     }
56     catch (const std::exception& e)
57     {
58         return {false,
59                 std::format("Error determining chassis status: {}", e.what())};
60     }
61 
62     return {true, ""};
63 }
64 
setPowerState(PowerState newPowerState,Services & services)65 void Chassis::setPowerState(PowerState newPowerState, Services& services)
66 {
67     verifyMonitoringInitialized();
68     auto [canSet, reason] = canSetPowerState(newPowerState);
69     if (!canSet)
70     {
71         throw std::runtime_error{
72             std::format("Unable to set chassis {} to state {}: {}", number,
73                         PowerInterface::toString(newPowerState), reason)};
74     }
75 
76     powerState = newPowerState;
77     if (powerState == PowerState::on)
78     {
79         powerOn(services);
80     }
81     else
82     {
83         powerOff(services);
84     }
85 }
86 
monitor(Services & services)87 void Chassis::monitor(Services& services)
88 {
89     verifyMonitoringInitialized();
90 
91     if (!isPresent() || !isInputPowerGood())
92     {
93         powerState = PowerState::off;
94         powerGood = PowerGood::off;
95         closeDevices();
96         return;
97     }
98 
99     if (isPresent() && isAvailable() && isInputPowerGood())
100     {
101         readPowerGood(services);
102         setInitialPowerStateIfNeeded();
103     }
104 }
105 
closeDevices()106 void Chassis::closeDevices()
107 {
108     for (auto& powerSequencer : powerSequencers)
109     {
110         try
111         {
112             if (powerSequencer->isOpen())
113             {
114                 powerSequencer->close();
115             }
116         }
117         catch (...)
118         {
119             // Ignore errors; often called when chassis goes missing/unavailable
120         }
121     }
122 }
123 
readPowerGood(Services & services)124 void Chassis::readPowerGood(Services& services)
125 {
126     // Count the number of power sequencer devices with power good on and off
127     size_t powerGoodOnCount{0}, powerGoodOffCount{0};
128     for (auto& powerSequencer : powerSequencers)
129     {
130         try
131         {
132             openDeviceIfNeeded(*powerSequencer, services);
133             if (powerSequencer->getPowerGood())
134             {
135                 ++powerGoodOnCount;
136             }
137             else
138             {
139                 ++powerGoodOffCount;
140             }
141         }
142         catch (...)
143         {}
144     }
145 
146     if (powerGoodOnCount == powerSequencers.size())
147     {
148         // All devices have power good on; set chassis power good to on
149         powerGood = PowerGood::on;
150     }
151     else if (powerGoodOffCount == powerSequencers.size())
152     {
153         // All devices have power good off; set chassis power good to off
154         powerGood = PowerGood::off;
155     }
156 }
157 
setInitialPowerStateIfNeeded()158 void Chassis::setInitialPowerStateIfNeeded()
159 {
160     if (!powerState)
161     {
162         if (powerGood)
163         {
164             if (powerGood == PowerGood::off)
165             {
166                 powerState = PowerState::off;
167             }
168             else
169             {
170                 powerState = PowerState::on;
171             }
172         }
173     }
174 }
175 
powerOn(Services & services)176 void Chassis::powerOn(Services& services)
177 {
178     std::string error{};
179     for (auto& powerSequencer : powerSequencers)
180     {
181         try
182         {
183             openDeviceIfNeeded(*powerSequencer, services);
184             powerSequencer->powerOn();
185         }
186         catch (const std::exception& e)
187         {
188             // Catch and save error so we can power on any remaining devices
189             error =
190                 std::format("Unable to power on device {} in chassis {}: {}",
191                             powerSequencer->getName(), number, e.what());
192         }
193     }
194 
195     if (!error.empty())
196     {
197         throw std::runtime_error{error};
198     }
199 }
200 
powerOff(Services & services)201 void Chassis::powerOff(Services& services)
202 {
203     std::string error{};
204     for (auto& powerSequencer : powerSequencers)
205     {
206         try
207         {
208             openDeviceIfNeeded(*powerSequencer, services);
209             powerSequencer->powerOff();
210         }
211         catch (const std::exception& e)
212         {
213             // Catch and save error so we can power off any remaining devices
214             error =
215                 std::format("Unable to power off device {} in chassis {}: {}",
216                             powerSequencer->getName(), number, e.what());
217         }
218     }
219 
220     if (!error.empty())
221     {
222         throw std::runtime_error{error};
223     }
224 }
225 
226 } // namespace phosphor::power::sequencer
227