17f88fe61SMatt Spinler /**
27f88fe61SMatt Spinler  * Copyright © 2017 IBM Corporation
37f88fe61SMatt Spinler  *
47f88fe61SMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
57f88fe61SMatt Spinler  * you may not use this file except in compliance with the License.
67f88fe61SMatt Spinler  * You may obtain a copy of the License at
77f88fe61SMatt Spinler  *
87f88fe61SMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
97f88fe61SMatt Spinler  *
107f88fe61SMatt Spinler  * Unless required by applicable law or agreed to in writing, software
117f88fe61SMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
127f88fe61SMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137f88fe61SMatt Spinler  * See the License for the specific language governing permissions and
147f88fe61SMatt Spinler  * limitations under the License.
157f88fe61SMatt Spinler  */
168600d9a0SMatthew Barth #include <chrono>
17618027abSDinesh Chinari #include <phosphor-logging/log.hpp>
18df3e8d67SMatthew Barth #include <phosphor-logging/elog.hpp>
19618027abSDinesh Chinari #include <phosphor-logging/elog-errors.hpp>
20618027abSDinesh Chinari #include <xyz/openbmc_project/Common/error.hpp>
217f88fe61SMatt Spinler #include "zone.hpp"
22df3e8d67SMatthew Barth #include "utility.hpp"
23d953bb25SMatthew Barth #include "sdbusplus.hpp"
247f88fe61SMatt Spinler 
257f88fe61SMatt Spinler namespace phosphor
267f88fe61SMatt Spinler {
277f88fe61SMatt Spinler namespace fan
287f88fe61SMatt Spinler {
297f88fe61SMatt Spinler namespace control
307f88fe61SMatt Spinler {
317f88fe61SMatt Spinler 
328600d9a0SMatthew Barth using namespace std::chrono;
339014980aSMatthew Barth using namespace phosphor::fan;
34df3e8d67SMatthew Barth using namespace phosphor::logging;
35618027abSDinesh Chinari using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
36618027abSDinesh Chinari                              Error::InternalFailure;
377f88fe61SMatt Spinler 
3814184131SMatthew Barth Zone::Zone(Mode mode,
3914184131SMatthew Barth            sdbusplus::bus::bus& bus,
408600d9a0SMatthew Barth            phosphor::fan::event::EventPtr& events,
417f88fe61SMatt Spinler            const ZoneDefinition& def) :
427f88fe61SMatt Spinler     _bus(bus),
437f88fe61SMatt Spinler     _fullSpeed(std::get<fullSpeedPos>(def)),
441de66629SMatthew Barth     _zoneNum(std::get<zoneNumPos>(def)),
45e0ca13ebSMatthew Barth     _defFloorSpeed(std::get<floorSpeedPos>(def)),
468600d9a0SMatthew Barth     _defCeilingSpeed(std::get<fullSpeedPos>(def)),
47a956184bSMatthew Barth     _incDelay(std::get<incDelayPos>(def)),
48a956184bSMatthew Barth     _decInterval(std::get<decIntervalPos>(def)),
491ee48f2bSMatthew Barth     _incTimer(events, [this](){ this->incTimerExpired(); }),
509014980aSMatthew Barth     _decTimer(events, [this](){ this->decTimerExpired(); }),
519014980aSMatthew Barth     _sdEvents(events)
527f88fe61SMatt Spinler {
537f88fe61SMatt Spinler     auto& fanDefs = std::get<fanListPos>(def);
547f88fe61SMatt Spinler 
557f88fe61SMatt Spinler     for (auto& def : fanDefs)
567f88fe61SMatt Spinler     {
577f88fe61SMatt Spinler         _fans.emplace_back(std::make_unique<Fan>(bus, def));
587f88fe61SMatt Spinler     }
5938a93a8aSMatthew Barth 
6014184131SMatthew Barth     // Do not enable set speed events when in init mode
6114184131SMatthew Barth     if (mode != Mode::init)
6214184131SMatthew Barth     {
63ccc7770eSMatthew Barth         // Setup signal trigger for set speed events
64ccc7770eSMatthew Barth         for (auto& event : std::get<setSpeedEventsPos>(def))
65ccc7770eSMatthew Barth         {
66ccc7770eSMatthew Barth             initEvent(event);
67ccc7770eSMatthew Barth         }
688600d9a0SMatthew Barth         // Start timer for fan speed decreases
69a956184bSMatthew Barth         if (!_decTimer.running() && _decInterval != seconds::zero())
708600d9a0SMatthew Barth         {
71a956184bSMatthew Barth             _decTimer.start(_decInterval,
72d953bb25SMatthew Barth                             util::Timer::TimerType::repeating);
738600d9a0SMatthew Barth         }
747f88fe61SMatt Spinler     }
7514184131SMatthew Barth }
767f88fe61SMatt Spinler 
777f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
787f88fe61SMatt Spinler {
7960b00766SMatthew Barth     if (_isActive)
8060b00766SMatthew Barth     {
8160b00766SMatthew Barth         _targetSpeed = speed;
827f88fe61SMatt Spinler         for (auto& fan : _fans)
837f88fe61SMatt Spinler         {
8460b00766SMatthew Barth             fan->setSpeed(_targetSpeed);
8560b00766SMatthew Barth         }
8660b00766SMatthew Barth     }
8760b00766SMatthew Barth }
8860b00766SMatthew Barth 
8960b00766SMatthew Barth void Zone::setFullSpeed()
9060b00766SMatthew Barth {
9160b00766SMatthew Barth     if (_fullSpeed != 0)
9260b00766SMatthew Barth     {
9360b00766SMatthew Barth         _targetSpeed = _fullSpeed;
9460b00766SMatthew Barth         for (auto& fan : _fans)
9560b00766SMatthew Barth         {
9660b00766SMatthew Barth             fan->setSpeed(_targetSpeed);
9760b00766SMatthew Barth         }
987f88fe61SMatt Spinler     }
997f88fe61SMatt Spinler }
1007f88fe61SMatt Spinler 
101861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
102861d77c3SMatthew Barth {
10360b00766SMatthew Barth     _active[*(group)] = isActiveAllow;
104861d77c3SMatthew Barth     if (!isActiveAllow)
105861d77c3SMatthew Barth     {
106861d77c3SMatthew Barth         _isActive = false;
107861d77c3SMatthew Barth     }
108861d77c3SMatthew Barth     else
109861d77c3SMatthew Barth     {
110861d77c3SMatthew Barth         // Check all entries are set to allow control active
111861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
112861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
113861d77c3SMatthew Barth                                 _active.end(),
114861d77c3SMatthew Barth                                 actPred);
115861d77c3SMatthew Barth     }
116861d77c3SMatthew Barth }
117861d77c3SMatthew Barth 
118b4a7cb99SMatthew Barth void Zone::setFloor(uint64_t speed)
119b4a7cb99SMatthew Barth {
120b4a7cb99SMatthew Barth     _floorSpeed = speed;
121b4a7cb99SMatthew Barth     // Floor speed above target, update target to floor speed
122b4a7cb99SMatthew Barth     if (_targetSpeed < _floorSpeed)
123b4a7cb99SMatthew Barth     {
124b4a7cb99SMatthew Barth         requestSpeedIncrease(_floorSpeed - _targetSpeed);
125b4a7cb99SMatthew Barth     }
126b4a7cb99SMatthew Barth }
127b4a7cb99SMatthew Barth 
128240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
129240397b9SMatthew Barth {
130240397b9SMatthew Barth     // Only increase speed when delta is higher than
131240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
132240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
133240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
134240397b9SMatthew Barth     {
1354e728542SMatthew Barth         auto requestTarget = getRequestSpeedBase();
13660b00766SMatthew Barth         requestTarget = (targetDelta - _incSpeedDelta) + requestTarget;
137240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
138240397b9SMatthew Barth         // Target speed can not go above a defined ceiling speed
13960b00766SMatthew Barth         if (requestTarget > _ceilingSpeed)
140240397b9SMatthew Barth         {
14160b00766SMatthew Barth             requestTarget = _ceilingSpeed;
142240397b9SMatthew Barth         }
1431ee48f2bSMatthew Barth         // Cancel current timer countdown
1441ee48f2bSMatthew Barth         if (_incTimer.running())
1451ee48f2bSMatthew Barth         {
1461ee48f2bSMatthew Barth             _incTimer.stop();
147240397b9SMatthew Barth         }
14860b00766SMatthew Barth         setSpeed(requestTarget);
1491ee48f2bSMatthew Barth         // Start timer countdown for fan speed increase
150a956184bSMatthew Barth         _incTimer.start(_incDelay,
151d953bb25SMatthew Barth                         util::Timer::TimerType::oneshot);
1521ee48f2bSMatthew Barth     }
1531ee48f2bSMatthew Barth }
1541ee48f2bSMatthew Barth 
1551ee48f2bSMatthew Barth void Zone::incTimerExpired()
1561ee48f2bSMatthew Barth {
1571ee48f2bSMatthew Barth     // Clear increase delta when timer expires allowing additional speed
1581ee48f2bSMatthew Barth     // increase requests or speed decreases to occur
159240397b9SMatthew Barth     _incSpeedDelta = 0;
160240397b9SMatthew Barth }
161240397b9SMatthew Barth 
1620ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1630ce99d8bSMatthew Barth {
1640ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1650ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1660ce99d8bSMatthew Barth     {
1670ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1680ce99d8bSMatthew Barth     }
1698600d9a0SMatthew Barth }
1700ce99d8bSMatthew Barth 
1718600d9a0SMatthew Barth void Zone::decTimerExpired()
1728600d9a0SMatthew Barth {
1731ee48f2bSMatthew Barth     // Only decrease speeds when no requested increases exist and
1741ee48f2bSMatthew Barth     // the increase timer is not running (i.e. not in the middle of increasing)
1751ee48f2bSMatthew Barth     if (_incSpeedDelta == 0 && !_incTimer.running())
1760ce99d8bSMatthew Barth     {
1774e728542SMatthew Barth         auto requestTarget = getRequestSpeedBase();
1780ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
17960b00766SMatthew Barth         if ((requestTarget < _decSpeedDelta) ||
18060b00766SMatthew Barth             (requestTarget - _decSpeedDelta < _floorSpeed))
1810ce99d8bSMatthew Barth         {
18260b00766SMatthew Barth             requestTarget = _floorSpeed;
1830ce99d8bSMatthew Barth         }
1840ce99d8bSMatthew Barth         else
1850ce99d8bSMatthew Barth         {
18660b00766SMatthew Barth             requestTarget = requestTarget - _decSpeedDelta;
1870ce99d8bSMatthew Barth         }
18860b00766SMatthew Barth         setSpeed(requestTarget);
1890ce99d8bSMatthew Barth     }
1900ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
1910ce99d8bSMatthew Barth     _decSpeedDelta = 0;
1928600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
1930ce99d8bSMatthew Barth }
1940ce99d8bSMatthew Barth 
195ccc7770eSMatthew Barth void Zone::initEvent(const SetSpeedEvent& event)
1961bf0ce4bSMatthew Barth {
197*336f18a5SMatthew Barth     sdbusplus::message::message nullMsg{nullptr};
198*336f18a5SMatthew Barth 
19967967f9aSMatthew Barth     for (auto& sig : std::get<signalsPos>(event))
2001bf0ce4bSMatthew Barth     {
201*336f18a5SMatthew Barth         // Initialize the event signal using handler
202*336f18a5SMatthew Barth         std::get<sigHandlerPos>(sig)(_bus, nullMsg, *this);
203*336f18a5SMatthew Barth         // Setup signal matches of the property for event
204f6b76d8eSMatthew Barth         std::unique_ptr<EventData> eventData =
2051bf0ce4bSMatthew Barth             std::make_unique<EventData>(
2061bf0ce4bSMatthew Barth                 EventData
2071bf0ce4bSMatthew Barth                 {
2081bf0ce4bSMatthew Barth                     std::get<groupPos>(event),
209*336f18a5SMatthew Barth                     std::get<sigMatchPos>(sig),
210*336f18a5SMatthew Barth                     std::get<sigHandlerPos>(sig),
211f9201abbSMatthew Barth                     std::get<actionsPos>(event)
212f6b76d8eSMatthew Barth                 }
213f6b76d8eSMatthew Barth             );
214*336f18a5SMatthew Barth         std::unique_ptr<sdbusplus::server::match::match> match = nullptr;
215*336f18a5SMatthew Barth         if (!std::get<sigMatchPos>(sig).empty())
216*336f18a5SMatthew Barth         {
217*336f18a5SMatthew Barth             match = std::make_unique<sdbusplus::server::match::match>(
2181bf0ce4bSMatthew Barth                     _bus,
219*336f18a5SMatthew Barth                     std::get<sigMatchPos>(sig).c_str(),
2201bf0ce4bSMatthew Barth                     std::bind(std::mem_fn(&Zone::handleEvent),
2211bf0ce4bSMatthew Barth                               this,
2221bf0ce4bSMatthew Barth                               std::placeholders::_1,
223f6b76d8eSMatthew Barth                               eventData.get())
224f6b76d8eSMatthew Barth                 );
225*336f18a5SMatthew Barth         }
226f6b76d8eSMatthew Barth         _signalEvents.emplace_back(std::move(eventData), std::move(match));
2271bf0ce4bSMatthew Barth     }
2289014980aSMatthew Barth     // Attach a timer to run the action of an event
2299014980aSMatthew Barth     auto eventTimer = std::get<timerPos>(event);
2309014980aSMatthew Barth     if (std::get<intervalPos>(eventTimer) != seconds(0))
2319014980aSMatthew Barth     {
2329014980aSMatthew Barth         std::unique_ptr<util::Timer> timer =
2339014980aSMatthew Barth             std::make_unique<util::Timer>(
2349014980aSMatthew Barth                 _sdEvents,
2359014980aSMatthew Barth                 [this,
236f9201abbSMatthew Barth                  action = &(std::get<actionsPos>(event)),
2379014980aSMatthew Barth                  group = &(std::get<groupPos>(event))]()
2389014980aSMatthew Barth                  {
2399014980aSMatthew Barth                      this->timerExpired(*group, *action);
2409014980aSMatthew Barth                  });
2419014980aSMatthew Barth         if (!timer->running())
2429014980aSMatthew Barth         {
2439014980aSMatthew Barth             timer->start(std::get<intervalPos>(eventTimer),
2449014980aSMatthew Barth                          util::Timer::TimerType::repeating);
2459014980aSMatthew Barth         }
2469014980aSMatthew Barth         _timerEvents.emplace_back(std::move(timer));
2479014980aSMatthew Barth     }
248f9201abbSMatthew Barth     // Run action functions for initial event state
249f9201abbSMatthew Barth     std::for_each(
250f9201abbSMatthew Barth         std::get<actionsPos>(event).begin(),
251f9201abbSMatthew Barth         std::get<actionsPos>(event).end(),
252f9201abbSMatthew Barth         [this, &event](auto const& action)
253f9201abbSMatthew Barth         {
254f9201abbSMatthew Barth             action(*this,
2551bf0ce4bSMatthew Barth                    std::get<groupPos>(event));
256f9201abbSMatthew Barth         });
2571bf0ce4bSMatthew Barth }
2581bf0ce4bSMatthew Barth 
259f6b76d8eSMatthew Barth void Zone::removeEvent(const SetSpeedEvent& event)
260f6b76d8eSMatthew Barth {
261f6b76d8eSMatthew Barth     // Find the signal event to be removed
262f6b76d8eSMatthew Barth     auto it = std::find_if(
263f6b76d8eSMatthew Barth         _signalEvents.begin(),
264f6b76d8eSMatthew Barth         _signalEvents.end(),
265f6b76d8eSMatthew Barth         [&event](auto const& se)
266f6b76d8eSMatthew Barth         {
267f6b76d8eSMatthew Barth             auto seEventData = *std::get<signalEventDataPos>(se);
268f9201abbSMatthew Barth             if (std::get<eventActionsPos>(seEventData).size() !=
269f9201abbSMatthew Barth                 std::get<actionsPos>(event).size())
270f9201abbSMatthew Barth             {
271f9201abbSMatthew Barth                 return false;
272f9201abbSMatthew Barth             }
273f9201abbSMatthew Barth             else
274f9201abbSMatthew Barth             {
275f9201abbSMatthew Barth                 // TODO openbmc/openbmc#2328 - Use the action function target
276f9201abbSMatthew Barth                 // for comparison
277f9201abbSMatthew Barth                 auto actsEqual = [](auto const& a1,
278f9201abbSMatthew Barth                                     auto const& a2)
279f9201abbSMatthew Barth                         {
280f9201abbSMatthew Barth                             return a1.target_type().name() ==
281f9201abbSMatthew Barth                                    a2.target_type().name();
282f9201abbSMatthew Barth                         };
283f6b76d8eSMatthew Barth                 return
284f6b76d8eSMatthew Barth                 (
285f6b76d8eSMatthew Barth                     std::get<eventGroupPos>(seEventData) ==
286f6b76d8eSMatthew Barth                         std::get<groupPos>(event) &&
287f9201abbSMatthew Barth                     std::equal(std::get<actionsPos>(event).begin(),
288f9201abbSMatthew Barth                                std::get<actionsPos>(event).end(),
289f9201abbSMatthew Barth                                std::get<eventActionsPos>(seEventData).begin(),
290f9201abbSMatthew Barth                                actsEqual)
291f6b76d8eSMatthew Barth                 );
292f9201abbSMatthew Barth             }
293f6b76d8eSMatthew Barth         });
294f6b76d8eSMatthew Barth     if (it != std::end(_signalEvents))
295f6b76d8eSMatthew Barth     {
296f6b76d8eSMatthew Barth         std::get<signalEventDataPos>(*it).reset();
297*336f18a5SMatthew Barth         if (std::get<signalMatchPos>(*it) != nullptr)
298*336f18a5SMatthew Barth         {
299f6b76d8eSMatthew Barth             std::get<signalMatchPos>(*it).reset();
300*336f18a5SMatthew Barth         }
301f6b76d8eSMatthew Barth         _signalEvents.erase(it);
302f6b76d8eSMatthew Barth     }
303f6b76d8eSMatthew Barth }
304f6b76d8eSMatthew Barth 
305f9201abbSMatthew Barth void Zone::timerExpired(Group eventGroup, std::vector<Action> eventActions)
3069014980aSMatthew Barth {
307f9201abbSMatthew Barth     // Perform the actions
308f9201abbSMatthew Barth     std::for_each(eventActions.begin(),
309f9201abbSMatthew Barth                   eventActions.end(),
310f9201abbSMatthew Barth                   [this, &eventGroup](auto const& action)
311f9201abbSMatthew Barth                   {
312f9201abbSMatthew Barth                       action(*this, eventGroup);
313f9201abbSMatthew Barth                   });
3149014980aSMatthew Barth }
3159014980aSMatthew Barth 
31638a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
31734f1bda2SMatthew Barth                        const EventData* eventData)
31838a93a8aSMatthew Barth {
31938a93a8aSMatthew Barth     // Handle the callback
32034f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
321f9201abbSMatthew Barth     // Perform the actions
322f9201abbSMatthew Barth     std::for_each(
323f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).begin(),
324f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).end(),
325f9201abbSMatthew Barth         [this, &eventData](auto const& action)
326f9201abbSMatthew Barth         {
327f9201abbSMatthew Barth             action(*this,
32834f1bda2SMatthew Barth                    std::get<eventGroupPos>(*eventData));
329f9201abbSMatthew Barth         });
33038a93a8aSMatthew Barth }
33138a93a8aSMatthew Barth 
3327f88fe61SMatt Spinler }
3337f88fe61SMatt Spinler }
3347f88fe61SMatt Spinler }
335