1 /**
2  * Copyright © 2021 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 "power_control.hpp"
18 
19 #include "types.hpp"
20 
21 #include <fmt/format.h>
22 
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/elog.hpp>
25 #include <phosphor-logging/log.hpp>
26 #include <xyz/openbmc_project/Common/error.hpp>
27 
28 #include <exception>
29 #include <string>
30 
31 using namespace phosphor::logging;
32 
33 namespace phosphor::power::sequencer
34 {
35 
36 const std::string powerControlLineName = "power-chassis-control";
37 const std::string pgoodLineName = "power-chassis-good";
38 
39 PowerControl::PowerControl(sdbusplus::bus::bus& bus,
40                            const sdeventplus::Event& event) :
41     PowerObject{bus, POWER_OBJ_PATH, true},
42     bus{bus}, timer{event, std::bind(&PowerControl::pollPgood, this),
43                     pollInterval}
44 {
45     // Obtain dbus service name
46     bus.request_name(POWER_IFACE);
47     setUpGpio();
48 }
49 
50 int PowerControl::getPgood() const
51 {
52     return pgood;
53 }
54 
55 int PowerControl::getPgoodTimeout() const
56 {
57     return timeout.count();
58 }
59 
60 int PowerControl::getState() const
61 {
62     return state;
63 }
64 
65 void PowerControl::pollPgood()
66 {
67     if (inStateTransition)
68     {
69         const auto now = std::chrono::steady_clock::now();
70         if (now > pgoodTimeoutTime)
71         {
72             log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
73             inStateTransition = false;
74             return;
75         }
76     }
77 
78     int pgoodState = pgoodLine.get_value();
79     if (pgoodState != pgood)
80     {
81         pgood = pgoodState;
82         if (pgoodState == 0)
83         {
84             emitPowerLostSignal();
85         }
86         else
87         {
88             emitPowerGoodSignal();
89         }
90         emitPropertyChangedSignal("pgood");
91     }
92     if (pgoodState == state)
93     {
94         inStateTransition = false;
95     }
96 }
97 
98 void PowerControl::setPgoodTimeout(int t)
99 {
100     if (timeout.count() != t)
101     {
102         timeout = std::chrono::seconds(t);
103         emitPropertyChangedSignal("pgood_timeout");
104     }
105 }
106 
107 void PowerControl::setState(int s)
108 {
109     if (state == s)
110     {
111         log<level::INFO>(
112             fmt::format("Power already at requested state: {}", state).c_str());
113         return;
114     }
115 
116     log<level::INFO>(fmt::format("setState: {}", s).c_str());
117     powerControlLine.request(
118         {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
119     powerControlLine.set_value(s);
120     powerControlLine.release();
121 
122     pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
123     inStateTransition = true;
124     state = s;
125     emitPropertyChangedSignal("state");
126 }
127 
128 void PowerControl::setUpGpio()
129 {
130     pgoodLine = gpiod::find_line(pgoodLineName);
131     if (!pgoodLine)
132     {
133         std::string errorString =
134             fmt::format("GPIO line name not found: {}", pgoodLineName);
135         log<level::ERR>(errorString.c_str());
136         report<
137             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
138         throw std::runtime_error(errorString);
139     }
140     powerControlLine = gpiod::find_line(powerControlLineName);
141     if (!powerControlLine)
142     {
143         std::string errorString =
144             fmt::format("GPIO line name not found: {}", powerControlLineName);
145         log<level::ERR>(errorString.c_str());
146         report<
147             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
148         throw std::runtime_error(errorString);
149     }
150 
151     pgoodLine.request(
152         {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
153     int pgoodState = pgoodLine.get_value();
154     pgood = pgoodState;
155     state = pgoodState;
156     log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
157 }
158 
159 } // namespace phosphor::power::sequencer
160