blob: 56c6cb670469d8749a4262f8c7e0f413c25305db [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 Barth90149802017-08-15 10:51:37 -050032using namespace phosphor::fan;
Matthew Barthdf3e8d62017-05-31 11:07:24 -050033using namespace phosphor::logging;
Dinesh Chinari618027a2017-06-26 23:26:50 -050034using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
35 Error::InternalFailure;
Matt Spinler7f88fe62017-04-10 14:39:02 -050036
Matthew Barth14184132017-05-19 14:37:30 -050037Zone::Zone(Mode mode,
38 sdbusplus::bus::bus& bus,
Matthew Barth8600d9a2017-06-23 14:38:05 -050039 phosphor::fan::event::EventPtr& events,
Matt Spinler7f88fe62017-04-10 14:39:02 -050040 const ZoneDefinition& def) :
41 _bus(bus),
42 _fullSpeed(std::get<fullSpeedPos>(def)),
Matthew Barth1de66622017-06-12 13:13:02 -050043 _zoneNum(std::get<zoneNumPos>(def)),
Matthew Barthe0ca13e2017-06-13 16:29:09 -050044 _defFloorSpeed(std::get<floorSpeedPos>(def)),
Matthew Barth8600d9a2017-06-23 14:38:05 -050045 _defCeilingSpeed(std::get<fullSpeedPos>(def)),
Matthew Bartha9561842017-06-29 11:43:45 -050046 _incDelay(std::get<incDelayPos>(def)),
47 _decInterval(std::get<decIntervalPos>(def)),
Matthew Barth1ee48f22017-06-27 15:14:48 -050048 _incTimer(events, [this](){ this->incTimerExpired(); }),
Matthew Barth90149802017-08-15 10:51:37 -050049 _decTimer(events, [this](){ this->decTimerExpired(); }),
50 _sdEvents(events)
Matt Spinler7f88fe62017-04-10 14:39:02 -050051{
52 auto& fanDefs = std::get<fanListPos>(def);
53
54 for (auto& def : fanDefs)
55 {
56 _fans.emplace_back(std::make_unique<Fan>(bus, def));
57 }
Matthew Barth38a93a82017-05-11 14:12:27 -050058
Matthew Barth14184132017-05-19 14:37:30 -050059 // Do not enable set speed events when in init mode
60 if (mode != Mode::init)
Matthew Barth17d1fe22017-05-11 15:00:36 -050061 {
Matthew Barthccc77702017-07-28 13:43:04 -050062 // Setup signal trigger for set speed events
63 for (auto& event : std::get<setSpeedEventsPos>(def))
64 {
65 initEvent(event);
66 }
Matthew Barth8600d9a2017-06-23 14:38:05 -050067 // Start timer for fan speed decreases
Matthew Bartha9561842017-06-29 11:43:45 -050068 if (!_decTimer.running() && _decInterval != seconds::zero())
Matthew Barth8600d9a2017-06-23 14:38:05 -050069 {
Matthew Bartha9561842017-06-29 11:43:45 -050070 _decTimer.start(_decInterval,
Matthew Barth8600d9a2017-06-23 14:38:05 -050071 phosphor::fan::util::Timer::TimerType::repeating);
72 }
Matthew Barth38a93a82017-05-11 14:12:27 -050073 }
Matt Spinler7f88fe62017-04-10 14:39:02 -050074}
75
Matt Spinler7f88fe62017-04-10 14:39:02 -050076void Zone::setSpeed(uint64_t speed)
77{
Matthew Barth60b00762017-08-15 13:39:06 -050078 if (_isActive)
Matt Spinler7f88fe62017-04-10 14:39:02 -050079 {
Matthew Barth60b00762017-08-15 13:39:06 -050080 _targetSpeed = speed;
81 for (auto& fan : _fans)
82 {
83 fan->setSpeed(_targetSpeed);
84 }
85 }
86}
87
88void Zone::setFullSpeed()
89{
90 if (_fullSpeed != 0)
91 {
92 _targetSpeed = _fullSpeed;
93 for (auto& fan : _fans)
94 {
95 fan->setSpeed(_targetSpeed);
96 }
Matt Spinler7f88fe62017-04-10 14:39:02 -050097 }
98}
99
Matthew Barth861d77c2017-05-22 14:18:25 -0500100void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
101{
Matthew Barth60b00762017-08-15 13:39:06 -0500102 _active[*(group)] = isActiveAllow;
Matthew Barth861d77c2017-05-22 14:18:25 -0500103 if (!isActiveAllow)
104 {
105 _isActive = false;
106 }
107 else
108 {
109 // Check all entries are set to allow control active
110 auto actPred = [](auto const& entry) {return entry.second;};
111 _isActive = std::all_of(_active.begin(),
112 _active.end(),
113 actPred);
114 }
115}
116
Matthew Barthb4a7cb92017-06-28 15:29:50 -0500117void Zone::setFloor(uint64_t speed)
118{
119 _floorSpeed = speed;
120 // Floor speed above target, update target to floor speed
121 if (_targetSpeed < _floorSpeed)
122 {
123 requestSpeedIncrease(_floorSpeed - _targetSpeed);
124 }
125}
126
Matthew Barth240397b2017-06-22 11:23:30 -0500127void Zone::requestSpeedIncrease(uint64_t targetDelta)
128{
129 // Only increase speed when delta is higher than
130 // the current increase delta for the zone and currently under ceiling
131 if (targetDelta > _incSpeedDelta &&
132 _targetSpeed < _ceilingSpeed)
133 {
Matthew Barth60b00762017-08-15 13:39:06 -0500134 auto requestTarget = _targetSpeed;
135 requestTarget = (targetDelta - _incSpeedDelta) + requestTarget;
Matthew Barth240397b2017-06-22 11:23:30 -0500136 _incSpeedDelta = targetDelta;
Matthew Barth240397b2017-06-22 11:23:30 -0500137 // Target speed can not go above a defined ceiling speed
Matthew Barth60b00762017-08-15 13:39:06 -0500138 if (requestTarget > _ceilingSpeed)
Matthew Barth240397b2017-06-22 11:23:30 -0500139 {
Matthew Barth60b00762017-08-15 13:39:06 -0500140 requestTarget = _ceilingSpeed;
Matthew Barth240397b2017-06-22 11:23:30 -0500141 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500142 // Cancel current timer countdown
143 if (_incTimer.running())
144 {
145 _incTimer.stop();
146 }
Matthew Barth60b00762017-08-15 13:39:06 -0500147 setSpeed(requestTarget);
Matthew Barth1ee48f22017-06-27 15:14:48 -0500148 // Start timer countdown for fan speed increase
Matthew Bartha9561842017-06-29 11:43:45 -0500149 _incTimer.start(_incDelay,
Matthew Barth1ee48f22017-06-27 15:14:48 -0500150 phosphor::fan::util::Timer::TimerType::oneshot);
Matthew Barth240397b2017-06-22 11:23:30 -0500151 }
Matthew Barth1ee48f22017-06-27 15:14:48 -0500152}
153
154void Zone::incTimerExpired()
155{
156 // Clear increase delta when timer expires allowing additional speed
157 // increase requests or speed decreases to occur
Matthew Barth240397b2017-06-22 11:23:30 -0500158 _incSpeedDelta = 0;
159}
160
Matthew Barth0ce99d82017-06-22 15:07:29 -0500161void Zone::requestSpeedDecrease(uint64_t targetDelta)
162{
163 // Only decrease the lowest target delta requested
164 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
165 {
166 _decSpeedDelta = targetDelta;
167 }
Matthew Barth8600d9a2017-06-23 14:38:05 -0500168}
Matthew Barth0ce99d82017-06-22 15:07:29 -0500169
Matthew Barth8600d9a2017-06-23 14:38:05 -0500170void Zone::decTimerExpired()
171{
Matthew Barth1ee48f22017-06-27 15:14:48 -0500172 // Only decrease speeds when no requested increases exist and
173 // the increase timer is not running (i.e. not in the middle of increasing)
174 if (_incSpeedDelta == 0 && !_incTimer.running())
Matthew Barth0ce99d82017-06-22 15:07:29 -0500175 {
Matthew Barth60b00762017-08-15 13:39:06 -0500176 auto requestTarget = _targetSpeed;
Matthew Barth0ce99d82017-06-22 15:07:29 -0500177 // Target speed can not go below the defined floor speed
Matthew Barth60b00762017-08-15 13:39:06 -0500178 if ((requestTarget < _decSpeedDelta) ||
179 (requestTarget - _decSpeedDelta < _floorSpeed))
Matthew Barth0ce99d82017-06-22 15:07:29 -0500180 {
Matthew Barth60b00762017-08-15 13:39:06 -0500181 requestTarget = _floorSpeed;
Matthew Barth0ce99d82017-06-22 15:07:29 -0500182 }
183 else
184 {
Matthew Barth60b00762017-08-15 13:39:06 -0500185 requestTarget = requestTarget - _decSpeedDelta;
Matthew Barth0ce99d82017-06-22 15:07:29 -0500186 }
Matthew Barth60b00762017-08-15 13:39:06 -0500187 setSpeed(requestTarget);
Matthew Barth0ce99d82017-06-22 15:07:29 -0500188 }
189 // Clear decrease delta when timer expires
190 _decSpeedDelta = 0;
Matthew Barth8600d9a2017-06-23 14:38:05 -0500191 // Decrease timer is restarted since its repeating
Matthew Barth0ce99d82017-06-22 15:07:29 -0500192}
193
Matthew Barthccc77702017-07-28 13:43:04 -0500194void Zone::initEvent(const SetSpeedEvent& event)
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500195{
Matthew Barthccc77702017-07-28 13:43:04 -0500196 // Get the current value for each property
Matthew Barth604329e2017-08-04 11:18:28 -0500197 for (auto& group : std::get<groupPos>(event))
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500198 {
Matthew Barth604329e2017-08-04 11:18:28 -0500199 try
200 {
201 refreshProperty(_bus,
202 group.first,
203 std::get<intfPos>(group.second),
204 std::get<propPos>(group.second));
205 }
206 catch (const InternalFailure& ife)
207 {
208 log<level::ERR>(
209 "Unable to find property: ",
210 entry("PATH=%s", group.first.c_str()),
211 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()),
212 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str()));
213 }
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500214 }
Matthew Barthccc77702017-07-28 13:43:04 -0500215 // Setup signal matches for property change events
216 for (auto& prop : std::get<propChangeListPos>(event))
217 {
Matthew Barthf6b76d82017-08-04 12:58:02 -0500218 std::unique_ptr<EventData> eventData =
219 std::make_unique<EventData>(
220 EventData
221 {
222 std::get<groupPos>(event),
223 std::get<handlerObjPos>(prop),
224 std::get<actionPos>(event)
225 }
226 );
227 std::unique_ptr<sdbusplus::server::match::match> match =
228 std::make_unique<sdbusplus::server::match::match>(
Matthew Barthccc77702017-07-28 13:43:04 -0500229 _bus,
230 std::get<signaturePos>(prop).c_str(),
231 std::bind(std::mem_fn(&Zone::handleEvent),
232 this,
233 std::placeholders::_1,
Matthew Barthf6b76d82017-08-04 12:58:02 -0500234 eventData.get())
235 );
236 _signalEvents.emplace_back(std::move(eventData), std::move(match));
Matthew Barthccc77702017-07-28 13:43:04 -0500237 }
Matthew Barth90149802017-08-15 10:51:37 -0500238 // Attach a timer to run the action of an event
239 auto eventTimer = std::get<timerPos>(event);
240 if (std::get<intervalPos>(eventTimer) != seconds(0))
241 {
242 std::unique_ptr<util::Timer> timer =
243 std::make_unique<util::Timer>(
244 _sdEvents,
245 [this,
246 action = &(std::get<actionPos>(event)),
247 group = &(std::get<groupPos>(event))]()
248 {
249 this->timerExpired(*group, *action);
250 });
251 if (!timer->running())
252 {
253 timer->start(std::get<intervalPos>(eventTimer),
254 util::Timer::TimerType::repeating);
255 }
256 _timerEvents.emplace_back(std::move(timer));
257 }
Matthew Barthccc77702017-07-28 13:43:04 -0500258 // Run action function for initial event state
259 std::get<actionPos>(event)(*this,
260 std::get<groupPos>(event));
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500261}
262
Matthew Barthf6b76d82017-08-04 12:58:02 -0500263void Zone::removeEvent(const SetSpeedEvent& event)
264{
265 // Find the signal event to be removed
266 auto it = std::find_if(
267 _signalEvents.begin(),
268 _signalEvents.end(),
269 [&event](auto const& se)
270 {
271 auto seEventData = *std::get<signalEventDataPos>(se);
272 // TODO Use the action function target for comparison
273 return
274 (
275 std::get<eventGroupPos>(seEventData) ==
276 std::get<groupPos>(event) &&
277 std::get<eventActionPos>(seEventData).target_type().name() ==
278 std::get<actionPos>(event).target_type().name()
279 );
280 });
281 if (it != std::end(_signalEvents))
282 {
283 std::get<signalEventDataPos>(*it).reset();
284 std::get<signalMatchPos>(*it).reset();
285 _signalEvents.erase(it);
286 }
287}
288
Matthew Barth1bf0ce42017-06-23 16:16:30 -0500289void Zone::refreshProperty(sdbusplus::bus::bus& bus,
290 const std::string& path,
291 const std::string& iface,
292 const std::string& prop)
293{
294 PropertyVariantType property;
295 getProperty(_bus, path, iface, prop, property);
296 setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
297}
298
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500299void Zone::getProperty(sdbusplus::bus::bus& bus,
300 const std::string& path,
301 const std::string& iface,
302 const std::string& prop,
Matthew Barth9e741ed2017-06-02 16:29:09 -0500303 PropertyVariantType& value)
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500304{
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500305 auto serv = phosphor::fan::util::getService(path, iface, bus);
306 auto hostCall = bus.new_method_call(serv.c_str(),
307 path.c_str(),
308 "org.freedesktop.DBus.Properties",
309 "Get");
310 hostCall.append(iface);
311 hostCall.append(prop);
312 auto hostResponseMsg = bus.call(hostCall);
313 if (hostResponseMsg.is_method_error())
314 {
Dinesh Chinari618027a2017-06-26 23:26:50 -0500315 log<level::ERR>("Error in host call response for retrieving property");
316 elog<InternalFailure>();
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500317 }
Matthew Barth9e741ed2017-06-02 16:29:09 -0500318 hostResponseMsg.read(value);
Matthew Barthdf3e8d62017-05-31 11:07:24 -0500319}
320
Matthew Barth90149802017-08-15 10:51:37 -0500321void Zone::timerExpired(Group eventGroup, Action eventAction)
322{
323 // Perform the action
324 eventAction(*this, eventGroup);
325}
326
Matthew Barth38a93a82017-05-11 14:12:27 -0500327void Zone::handleEvent(sdbusplus::message::message& msg,
Matthew Barth34f1bda2017-05-31 13:45:36 -0500328 const EventData* eventData)
Matthew Barth38a93a82017-05-11 14:12:27 -0500329{
330 // Handle the callback
Matthew Barth34f1bda2017-05-31 13:45:36 -0500331 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
Matthew Barth17d1fe22017-05-11 15:00:36 -0500332 // Perform the action
Matthew Barth34f1bda2017-05-31 13:45:36 -0500333 std::get<eventActionPos>(*eventData)(*this,
334 std::get<eventGroupPos>(*eventData));
Matthew Barth38a93a82017-05-11 14:12:27 -0500335}
336
Matt Spinler7f88fe62017-04-10 14:39:02 -0500337}
338}
339}