blob: 8f113939b0697d6664b295f37e3977d3ed96c471 [file] [log] [blame]
Matt Spinler7f88fe62017-04-10 14:39:02 -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 */
Matthew Barth8600d9a2017-06-23 14:38:05 -050016#include <chrono>
Dinesh Chinari618027a2017-06-26 23:26:50 -050017#include <phosphor-logging/log.hpp>
Matthew Barthdf3e8d62017-05-31 11:07:24 -050018#include <phosphor-logging/elog.hpp>
Dinesh Chinari618027a2017-06-26 23:26:50 -050019#include <phosphor-logging/elog-errors.hpp>
20#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler7f88fe62017-04-10 14:39:02 -050021#include "zone.hpp"
Matthew Barthdf3e8d62017-05-31 11:07:24 -050022#include "utility.hpp"
Matt Spinler7f88fe62017-04-10 14:39:02 -050023
24namespace phosphor
25{
26namespace fan
27{
28namespace control
29{
30
Matthew Barth8600d9a2017-06-23 14:38:05 -050031using namespace std::chrono;
Matthew Barthdf3e8d62017-05-31 11:07:24 -050032using namespace phosphor::logging;
Dinesh Chinari618027a2017-06-26 23:26:50 -050033using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
34 Error::InternalFailure;
Matt Spinler7f88fe62017-04-10 14:39:02 -050035
Matthew Barth14184132017-05-19 14:37:30 -050036Zone::Zone(Mode mode,
37 sdbusplus::bus::bus& bus,
Matthew Barth8600d9a2017-06-23 14:38:05 -050038 phosphor::fan::event::EventPtr& events,
Matt Spinler7f88fe62017-04-10 14:39:02 -050039 const ZoneDefinition& def) :
40 _bus(bus),
41 _fullSpeed(std::get<fullSpeedPos>(def)),
Matthew Barth1de66622017-06-12 13:13:02 -050042 _zoneNum(std::get<zoneNumPos>(def)),
Matthew Barthe0ca13e2017-06-13 16:29:09 -050043 _defFloorSpeed(std::get<floorSpeedPos>(def)),
Matthew Barth8600d9a2017-06-23 14:38:05 -050044 _defCeilingSpeed(std::get<fullSpeedPos>(def)),
Matthew Barth1ee48f22017-06-27 15:14:48 -050045 _incTimer(events, [this](){ this->incTimerExpired(); }),
Matthew Barth8600d9a2017-06-23 14:38:05 -050046 _decTimer(events, [this](){ this->decTimerExpired(); })
Matt Spinler7f88fe62017-04-10 14:39:02 -050047{
48 auto& fanDefs = std::get<fanListPos>(def);
49
50 for (auto& def : fanDefs)
51 {
52 _fans.emplace_back(std::make_unique<Fan>(bus, def));
53 }
Matthew Barth38a93a82017-05-11 14:12:27 -050054
Matthew Barth14184132017-05-19 14:37:30 -050055 // Do not enable set speed events when in init mode
56 if (mode != Mode::init)
Matthew Barth17d1fe22017-05-11 15:00:36 -050057 {
Matthew Barth1bf0ce42017-06-23 16:16:30 -050058 initEvents(def);
Matthew Barth8600d9a2017-06-23 14:38:05 -050059 // Start timer for fan speed decreases
60 if (!_decTimer.running())
61 {
62 //TODO Update time value to what's given in zones yaml
63 _decTimer.start(seconds(30),
64 phosphor::fan::util::Timer::TimerType::repeating);
65 }
Matthew Barth38a93a82017-05-11 14:12:27 -050066 }
Matt Spinler7f88fe62017-04-10 14:39:02 -050067}
68
69
70void Zone::setSpeed(uint64_t speed)
71{
72 for (auto& fan : _fans)
73 {
74 fan->setSpeed(speed);
75 }
76}
77
Matthew Barth861d77c2017-05-22 14:18:25 -050078void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
79{
80 _active[group] = isActiveAllow;
81 if (!isActiveAllow)
82 {
83 _isActive = false;
84 }
85 else
86 {
87 // Check all entries are set to allow control active
88 auto actPred = [](auto const& entry) {return entry.second;};
89 _isActive = std::all_of(_active.begin(),
90 _active.end(),
91 actPred);
92 }
93}
94
Matthew Barthb4a7cb92017-06-28 15:29:50 -050095void Zone::setFloor(uint64_t speed)
96{
97 _floorSpeed = speed;
98 // Floor speed above target, update target to floor speed
99 if (_targetSpeed < _floorSpeed)
100 {
101 requestSpeedIncrease(_floorSpeed - _targetSpeed);
102 }
103}
104
Matthew Barth240397b2017-06-22 11:23:30 -0500105void Zone::requestSpeedIncrease(uint64_t targetDelta)
106{
107 // Only increase speed when delta is higher than
108 // the current increase delta for the zone and currently under ceiling
109 if (targetDelta > _incSpeedDelta &&
110 _targetSpeed < _ceilingSpeed)
111 {
112 _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
113 _incSpeedDelta = targetDelta;
Matthew Barth240397b2017-06-22 11:23:30 -0500114 // Target speed can not go above a defined ceiling speed
115 if (_targetSpeed > _ceilingSpeed)
116 {
117 _targetSpeed = _ceilingSpeed;
118 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500119 // Cancel current timer countdown
120 if (_incTimer.running())
121 {
122 _incTimer.stop();
123 }
Matthew Barth240397b2017-06-22 11:23:30 -0500124 setSpeed(_targetSpeed);
Matthew Barth1ee48f22017-06-27 15:14:48 -0500125 // Start timer countdown for fan speed increase
126 //TODO Update time value to what's given in zones yaml
127 _incTimer.start(seconds(5),
128 phosphor::fan::util::Timer::TimerType::oneshot);
Matthew Barth240397b2017-06-22 11:23:30 -0500129 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500130}
131
132void Zone::incTimerExpired()
133{
134 // Clear increase delta when timer expires allowing additional speed
135 // increase requests or speed decreases to occur
Matthew Barth240397b2017-06-22 11:23:30 -0500136 _incSpeedDelta = 0;
137}
138
Matthew Barth0ce99d82017-06-22 15:07:29 -0500139void Zone::requestSpeedDecrease(uint64_t targetDelta)
140{
141 // Only decrease the lowest target delta requested
142 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
143 {
144 _decSpeedDelta = targetDelta;
145 }
Matthew Barth8600d9a2017-06-23 14:38:05 -0500146}
Matthew Barth0ce99d82017-06-22 15:07:29 -0500147
Matthew Barth8600d9a2017-06-23 14:38:05 -0500148void Zone::decTimerExpired()
149{
Matthew Barth1ee48f22017-06-27 15:14:48 -0500150 // Only decrease speeds when no requested increases exist and
151 // the increase timer is not running (i.e. not in the middle of increasing)
152 if (_incSpeedDelta == 0 && !_incTimer.running())
Matthew Barth0ce99d82017-06-22 15:07:29 -0500153 {
154 // Target speed can not go below the defined floor speed
155 if ((_targetSpeed < _decSpeedDelta) ||
156 (_targetSpeed - _decSpeedDelta < _floorSpeed))
157 {
158 _targetSpeed = _floorSpeed;
159 }
160 else
161 {
162 _targetSpeed = _targetSpeed - _decSpeedDelta;
163 }
164 setSpeed(_targetSpeed);
165 }
166 // Clear decrease delta when timer expires
167 _decSpeedDelta = 0;
Matthew Barth8600d9a2017-06-23 14:38:05 -0500168 // Decrease timer is restarted since its repeating
Matthew Barth0ce99d82017-06-22 15:07:29 -0500169}
170
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500171void Zone::initEvents(const ZoneDefinition& def)
172{
173 // Setup signal trigger for set speed events
174 for (auto& event : std::get<setSpeedEventsPos>(def))
175 {
176 // Get the current value for each property
177 for (auto& entry : std::get<groupPos>(event))
178 {
179 refreshProperty(_bus,
180 entry.first,
181 std::get<intfPos>(entry.second),
182 std::get<propPos>(entry.second));
183 }
184 // Setup signal matches for property change events
185 for (auto& prop : std::get<propChangeListPos>(event))
186 {
187 _signalEvents.emplace_back(
188 std::make_unique<EventData>(
189 EventData
190 {
191 std::get<groupPos>(event),
192 std::get<handlerObjPos>(prop),
193 std::get<actionPos>(event)
194 }));
195 _matches.emplace_back(
196 _bus,
197 std::get<signaturePos>(prop).c_str(),
198 std::bind(std::mem_fn(&Zone::handleEvent),
199 this,
200 std::placeholders::_1,
201 _signalEvents.back().get()));
202 }
203 // Run action function for initial event state
204 std::get<actionPos>(event)(*this,
205 std::get<groupPos>(event));
206 }
207}
208
209void Zone::refreshProperty(sdbusplus::bus::bus& bus,
210 const std::string& path,
211 const std::string& iface,
212 const std::string& prop)
213{
214 PropertyVariantType property;
215 getProperty(_bus, path, iface, prop, property);
216 setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
217}
218
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500219void Zone::getProperty(sdbusplus::bus::bus& bus,
220 const std::string& path,
221 const std::string& iface,
222 const std::string& prop,
Matthew Barth9e741ed2017-06-02 16:29:09 -0500223 PropertyVariantType& value)
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500224{
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500225 auto serv = phosphor::fan::util::getService(path, iface, bus);
226 auto hostCall = bus.new_method_call(serv.c_str(),
227 path.c_str(),
228 "org.freedesktop.DBus.Properties",
229 "Get");
230 hostCall.append(iface);
231 hostCall.append(prop);
232 auto hostResponseMsg = bus.call(hostCall);
233 if (hostResponseMsg.is_method_error())
234 {
Dinesh Chinari618027a2017-06-26 23:26:50 -0500235 log<level::ERR>("Error in host call response for retrieving property");
236 elog<InternalFailure>();
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500237 }
Matthew Barth9e741ed2017-06-02 16:29:09 -0500238 hostResponseMsg.read(value);
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500239}
240
Matthew Barth38a93a82017-05-11 14:12:27 -0500241void Zone::handleEvent(sdbusplus::message::message& msg,
Matthew Barth34f1bda2017-05-31 13:45:36 -0500242 const EventData* eventData)
Matthew Barth38a93a82017-05-11 14:12:27 -0500243{
244 // Handle the callback
Matthew Barth34f1bda2017-05-31 13:45:36 -0500245 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
Matthew Barth17d1fe22017-05-11 15:00:36 -0500246 // Perform the action
Matthew Barth34f1bda2017-05-31 13:45:36 -0500247 std::get<eventActionPos>(*eventData)(*this,
248 std::get<eventGroupPos>(*eventData));
Matthew Barth38a93a82017-05-11 14:12:27 -0500249}
250
Matt Spinler7f88fe62017-04-10 14:39:02 -0500251}
252}
253}