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 {
1971bf0ce4bSMatthew Barth     // Get the current value for each property
198604329efSMatthew Barth     for (auto& group : std::get<groupPos>(event))
199604329efSMatthew Barth     {
200604329efSMatthew Barth         try
2011bf0ce4bSMatthew Barth         {
2021bf0ce4bSMatthew Barth             refreshProperty(_bus,
203604329efSMatthew Barth                             group.first,
204604329efSMatthew Barth                             std::get<intfPos>(group.second),
205604329efSMatthew Barth                             std::get<propPos>(group.second));
206604329efSMatthew Barth         }
207604329efSMatthew Barth         catch (const InternalFailure& ife)
208604329efSMatthew Barth         {
209d953bb25SMatthew Barth             log<level::INFO>(
210d953bb25SMatthew Barth                 "Unable to find property",
211604329efSMatthew Barth                 entry("PATH=%s", group.first.c_str()),
212604329efSMatthew Barth                 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()),
213604329efSMatthew Barth                 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str()));
214604329efSMatthew Barth         }
2151bf0ce4bSMatthew Barth     }
216*67967f9aSMatthew Barth     // Setup signal matches for events
217*67967f9aSMatthew Barth     for (auto& sig : std::get<signalsPos>(event))
2181bf0ce4bSMatthew Barth     {
219f6b76d8eSMatthew Barth         std::unique_ptr<EventData> eventData =
2201bf0ce4bSMatthew Barth             std::make_unique<EventData>(
2211bf0ce4bSMatthew Barth                 EventData
2221bf0ce4bSMatthew Barth                 {
2231bf0ce4bSMatthew Barth                     std::get<groupPos>(event),
224*67967f9aSMatthew Barth                     std::get<handlerObjPos>(sig),
225f9201abbSMatthew Barth                     std::get<actionsPos>(event)
226f6b76d8eSMatthew Barth                 }
227f6b76d8eSMatthew Barth             );
228f6b76d8eSMatthew Barth         std::unique_ptr<sdbusplus::server::match::match> match =
229f6b76d8eSMatthew Barth             std::make_unique<sdbusplus::server::match::match>(
2301bf0ce4bSMatthew Barth                 _bus,
231*67967f9aSMatthew Barth                 std::get<signaturePos>(sig).c_str(),
2321bf0ce4bSMatthew Barth                 std::bind(std::mem_fn(&Zone::handleEvent),
2331bf0ce4bSMatthew Barth                           this,
2341bf0ce4bSMatthew Barth                           std::placeholders::_1,
235f6b76d8eSMatthew Barth                           eventData.get())
236f6b76d8eSMatthew Barth             );
237f6b76d8eSMatthew Barth         _signalEvents.emplace_back(std::move(eventData), std::move(match));
2381bf0ce4bSMatthew Barth     }
2399014980aSMatthew Barth     // Attach a timer to run the action of an event
2409014980aSMatthew Barth     auto eventTimer = std::get<timerPos>(event);
2419014980aSMatthew Barth     if (std::get<intervalPos>(eventTimer) != seconds(0))
2429014980aSMatthew Barth     {
2439014980aSMatthew Barth         std::unique_ptr<util::Timer> timer =
2449014980aSMatthew Barth             std::make_unique<util::Timer>(
2459014980aSMatthew Barth                 _sdEvents,
2469014980aSMatthew Barth                 [this,
247f9201abbSMatthew Barth                  action = &(std::get<actionsPos>(event)),
2489014980aSMatthew Barth                  group = &(std::get<groupPos>(event))]()
2499014980aSMatthew Barth                  {
2509014980aSMatthew Barth                      this->timerExpired(*group, *action);
2519014980aSMatthew Barth                  });
2529014980aSMatthew Barth         if (!timer->running())
2539014980aSMatthew Barth         {
2549014980aSMatthew Barth             timer->start(std::get<intervalPos>(eventTimer),
2559014980aSMatthew Barth                          util::Timer::TimerType::repeating);
2569014980aSMatthew Barth         }
2579014980aSMatthew Barth         _timerEvents.emplace_back(std::move(timer));
2589014980aSMatthew Barth     }
259f9201abbSMatthew Barth     // Run action functions for initial event state
260f9201abbSMatthew Barth     std::for_each(
261f9201abbSMatthew Barth         std::get<actionsPos>(event).begin(),
262f9201abbSMatthew Barth         std::get<actionsPos>(event).end(),
263f9201abbSMatthew Barth         [this, &event](auto const& action)
264f9201abbSMatthew Barth         {
265f9201abbSMatthew Barth             action(*this,
2661bf0ce4bSMatthew Barth                    std::get<groupPos>(event));
267f9201abbSMatthew Barth         });
2681bf0ce4bSMatthew Barth }
2691bf0ce4bSMatthew Barth 
270f6b76d8eSMatthew Barth void Zone::removeEvent(const SetSpeedEvent& event)
271f6b76d8eSMatthew Barth {
272f6b76d8eSMatthew Barth     // Find the signal event to be removed
273f6b76d8eSMatthew Barth     auto it = std::find_if(
274f6b76d8eSMatthew Barth         _signalEvents.begin(),
275f6b76d8eSMatthew Barth         _signalEvents.end(),
276f6b76d8eSMatthew Barth         [&event](auto const& se)
277f6b76d8eSMatthew Barth         {
278f6b76d8eSMatthew Barth             auto seEventData = *std::get<signalEventDataPos>(se);
279f9201abbSMatthew Barth             if (std::get<eventActionsPos>(seEventData).size() !=
280f9201abbSMatthew Barth                 std::get<actionsPos>(event).size())
281f9201abbSMatthew Barth             {
282f9201abbSMatthew Barth                 return false;
283f9201abbSMatthew Barth             }
284f9201abbSMatthew Barth             else
285f9201abbSMatthew Barth             {
286f9201abbSMatthew Barth                 // TODO openbmc/openbmc#2328 - Use the action function target
287f9201abbSMatthew Barth                 // for comparison
288f9201abbSMatthew Barth                 auto actsEqual = [](auto const& a1,
289f9201abbSMatthew Barth                                     auto const& a2)
290f9201abbSMatthew Barth                         {
291f9201abbSMatthew Barth                             return a1.target_type().name() ==
292f9201abbSMatthew Barth                                    a2.target_type().name();
293f9201abbSMatthew Barth                         };
294f6b76d8eSMatthew Barth                 return
295f6b76d8eSMatthew Barth                 (
296f6b76d8eSMatthew Barth                     std::get<eventGroupPos>(seEventData) ==
297f6b76d8eSMatthew Barth                         std::get<groupPos>(event) &&
298f9201abbSMatthew Barth                     std::equal(std::get<actionsPos>(event).begin(),
299f9201abbSMatthew Barth                                std::get<actionsPos>(event).end(),
300f9201abbSMatthew Barth                                std::get<eventActionsPos>(seEventData).begin(),
301f9201abbSMatthew Barth                                actsEqual)
302f6b76d8eSMatthew Barth                 );
303f9201abbSMatthew Barth             }
304f6b76d8eSMatthew Barth         });
305f6b76d8eSMatthew Barth     if (it != std::end(_signalEvents))
306f6b76d8eSMatthew Barth     {
307f6b76d8eSMatthew Barth         std::get<signalEventDataPos>(*it).reset();
308f6b76d8eSMatthew Barth         std::get<signalMatchPos>(*it).reset();
309f6b76d8eSMatthew Barth         _signalEvents.erase(it);
310f6b76d8eSMatthew Barth     }
311f6b76d8eSMatthew Barth }
312f6b76d8eSMatthew Barth 
3131bf0ce4bSMatthew Barth void Zone::refreshProperty(sdbusplus::bus::bus& bus,
3141bf0ce4bSMatthew Barth                            const std::string& path,
3151bf0ce4bSMatthew Barth                            const std::string& iface,
3161bf0ce4bSMatthew Barth                            const std::string& prop)
3171bf0ce4bSMatthew Barth {
3181bf0ce4bSMatthew Barth     PropertyVariantType property;
3191bf0ce4bSMatthew Barth     getProperty(_bus, path, iface, prop, property);
3201bf0ce4bSMatthew Barth     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
3211bf0ce4bSMatthew Barth }
3221bf0ce4bSMatthew Barth 
323df3e8d67SMatthew Barth void Zone::getProperty(sdbusplus::bus::bus& bus,
324df3e8d67SMatthew Barth                        const std::string& path,
325df3e8d67SMatthew Barth                        const std::string& iface,
326df3e8d67SMatthew Barth                        const std::string& prop,
3279e741ed0SMatthew Barth                        PropertyVariantType& value)
328df3e8d67SMatthew Barth {
329d953bb25SMatthew Barth     auto serv = util::SDBusPlus::getService(bus, path, iface);
330df3e8d67SMatthew Barth     auto hostCall = bus.new_method_call(serv.c_str(),
331df3e8d67SMatthew Barth                                         path.c_str(),
332df3e8d67SMatthew Barth                                         "org.freedesktop.DBus.Properties",
333df3e8d67SMatthew Barth                                         "Get");
334df3e8d67SMatthew Barth     hostCall.append(iface);
335df3e8d67SMatthew Barth     hostCall.append(prop);
336df3e8d67SMatthew Barth     auto hostResponseMsg = bus.call(hostCall);
337df3e8d67SMatthew Barth     if (hostResponseMsg.is_method_error())
338df3e8d67SMatthew Barth     {
339d953bb25SMatthew Barth         log<level::INFO>("Host call response error for retrieving property");
340618027abSDinesh Chinari         elog<InternalFailure>();
341df3e8d67SMatthew Barth     }
3429e741ed0SMatthew Barth     hostResponseMsg.read(value);
343df3e8d67SMatthew Barth }
344df3e8d67SMatthew Barth 
345f9201abbSMatthew Barth void Zone::timerExpired(Group eventGroup, std::vector<Action> eventActions)
3469014980aSMatthew Barth {
347f9201abbSMatthew Barth     // Perform the actions
348f9201abbSMatthew Barth     std::for_each(eventActions.begin(),
349f9201abbSMatthew Barth                   eventActions.end(),
350f9201abbSMatthew Barth                   [this, &eventGroup](auto const& action)
351f9201abbSMatthew Barth                   {
352f9201abbSMatthew Barth                       action(*this, eventGroup);
353f9201abbSMatthew Barth                   });
3549014980aSMatthew Barth }
3559014980aSMatthew Barth 
35638a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
35734f1bda2SMatthew Barth                        const EventData* eventData)
35838a93a8aSMatthew Barth {
35938a93a8aSMatthew Barth     // Handle the callback
36034f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
361f9201abbSMatthew Barth     // Perform the actions
362f9201abbSMatthew Barth     std::for_each(
363f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).begin(),
364f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).end(),
365f9201abbSMatthew Barth         [this, &eventData](auto const& action)
366f9201abbSMatthew Barth         {
367f9201abbSMatthew Barth             action(*this,
36834f1bda2SMatthew Barth                    std::get<eventGroupPos>(*eventData));
369f9201abbSMatthew Barth         });
37038a93a8aSMatthew Barth }
37138a93a8aSMatthew Barth 
3727f88fe61SMatt Spinler }
3737f88fe61SMatt Spinler }
3747f88fe61SMatt Spinler }
375