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"
237f88fe61SMatt Spinler 
247f88fe61SMatt Spinler namespace phosphor
257f88fe61SMatt Spinler {
267f88fe61SMatt Spinler namespace fan
277f88fe61SMatt Spinler {
287f88fe61SMatt Spinler namespace control
297f88fe61SMatt Spinler {
307f88fe61SMatt Spinler 
318600d9a0SMatthew Barth using namespace std::chrono;
32df3e8d67SMatthew Barth using namespace phosphor::logging;
33618027abSDinesh Chinari using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
34618027abSDinesh Chinari                              Error::InternalFailure;
357f88fe61SMatt Spinler 
3614184131SMatthew Barth Zone::Zone(Mode mode,
3714184131SMatthew Barth            sdbusplus::bus::bus& bus,
388600d9a0SMatthew Barth            phosphor::fan::event::EventPtr& events,
397f88fe61SMatt Spinler            const ZoneDefinition& def) :
407f88fe61SMatt Spinler     _bus(bus),
417f88fe61SMatt Spinler     _fullSpeed(std::get<fullSpeedPos>(def)),
421de66629SMatthew Barth     _zoneNum(std::get<zoneNumPos>(def)),
43e0ca13ebSMatthew Barth     _defFloorSpeed(std::get<floorSpeedPos>(def)),
448600d9a0SMatthew Barth     _defCeilingSpeed(std::get<fullSpeedPos>(def)),
45*a956184bSMatthew Barth     _incDelay(std::get<incDelayPos>(def)),
46*a956184bSMatthew Barth     _decInterval(std::get<decIntervalPos>(def)),
471ee48f2bSMatthew Barth     _incTimer(events, [this](){ this->incTimerExpired(); }),
488600d9a0SMatthew Barth     _decTimer(events, [this](){ this->decTimerExpired(); })
497f88fe61SMatt Spinler {
507f88fe61SMatt Spinler     auto& fanDefs = std::get<fanListPos>(def);
517f88fe61SMatt Spinler 
527f88fe61SMatt Spinler     for (auto& def : fanDefs)
537f88fe61SMatt Spinler     {
547f88fe61SMatt Spinler         _fans.emplace_back(std::make_unique<Fan>(bus, def));
557f88fe61SMatt Spinler     }
5638a93a8aSMatthew Barth 
5714184131SMatthew Barth     // Do not enable set speed events when in init mode
5814184131SMatthew Barth     if (mode != Mode::init)
5914184131SMatthew Barth     {
601bf0ce4bSMatthew Barth         initEvents(def);
618600d9a0SMatthew Barth         // Start timer for fan speed decreases
62*a956184bSMatthew Barth         if (!_decTimer.running() && _decInterval != seconds::zero())
638600d9a0SMatthew Barth         {
64*a956184bSMatthew Barth             _decTimer.start(_decInterval,
658600d9a0SMatthew Barth                             phosphor::fan::util::Timer::TimerType::repeating);
668600d9a0SMatthew Barth         }
677f88fe61SMatt Spinler     }
6814184131SMatthew Barth }
697f88fe61SMatt Spinler 
707f88fe61SMatt Spinler 
717f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
727f88fe61SMatt Spinler {
737f88fe61SMatt Spinler     for (auto& fan : _fans)
747f88fe61SMatt Spinler     {
757f88fe61SMatt Spinler         fan->setSpeed(speed);
767f88fe61SMatt Spinler     }
777f88fe61SMatt Spinler }
787f88fe61SMatt Spinler 
79861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
80861d77c3SMatthew Barth {
81861d77c3SMatthew Barth     _active[group] = isActiveAllow;
82861d77c3SMatthew Barth     if (!isActiveAllow)
83861d77c3SMatthew Barth     {
84861d77c3SMatthew Barth         _isActive = false;
85861d77c3SMatthew Barth     }
86861d77c3SMatthew Barth     else
87861d77c3SMatthew Barth     {
88861d77c3SMatthew Barth         // Check all entries are set to allow control active
89861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
90861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
91861d77c3SMatthew Barth                                 _active.end(),
92861d77c3SMatthew Barth                                 actPred);
93861d77c3SMatthew Barth     }
94861d77c3SMatthew Barth }
95861d77c3SMatthew Barth 
96b4a7cb99SMatthew Barth void Zone::setFloor(uint64_t speed)
97b4a7cb99SMatthew Barth {
98b4a7cb99SMatthew Barth     _floorSpeed = speed;
99b4a7cb99SMatthew Barth     // Floor speed above target, update target to floor speed
100b4a7cb99SMatthew Barth     if (_targetSpeed < _floorSpeed)
101b4a7cb99SMatthew Barth     {
102b4a7cb99SMatthew Barth         requestSpeedIncrease(_floorSpeed - _targetSpeed);
103b4a7cb99SMatthew Barth     }
104b4a7cb99SMatthew Barth }
105b4a7cb99SMatthew Barth 
106240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
107240397b9SMatthew Barth {
108240397b9SMatthew Barth     // Only increase speed when delta is higher than
109240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
110240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
111240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
112240397b9SMatthew Barth     {
113240397b9SMatthew Barth         _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
114240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
115240397b9SMatthew Barth         // Target speed can not go above a defined ceiling speed
116240397b9SMatthew Barth         if (_targetSpeed > _ceilingSpeed)
117240397b9SMatthew Barth         {
118240397b9SMatthew Barth             _targetSpeed = _ceilingSpeed;
119240397b9SMatthew Barth         }
1201ee48f2bSMatthew Barth         // Cancel current timer countdown
1211ee48f2bSMatthew Barth         if (_incTimer.running())
1221ee48f2bSMatthew Barth         {
1231ee48f2bSMatthew Barth             _incTimer.stop();
124240397b9SMatthew Barth         }
1251ee48f2bSMatthew Barth         setSpeed(_targetSpeed);
1261ee48f2bSMatthew Barth         // Start timer countdown for fan speed increase
127*a956184bSMatthew Barth         _incTimer.start(_incDelay,
1281ee48f2bSMatthew Barth                         phosphor::fan::util::Timer::TimerType::oneshot);
1291ee48f2bSMatthew Barth     }
1301ee48f2bSMatthew Barth }
1311ee48f2bSMatthew Barth 
1321ee48f2bSMatthew Barth void Zone::incTimerExpired()
1331ee48f2bSMatthew Barth {
1341ee48f2bSMatthew Barth     // Clear increase delta when timer expires allowing additional speed
1351ee48f2bSMatthew Barth     // increase requests or speed decreases to occur
136240397b9SMatthew Barth     _incSpeedDelta = 0;
137240397b9SMatthew Barth }
138240397b9SMatthew Barth 
1390ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1400ce99d8bSMatthew Barth {
1410ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1420ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1430ce99d8bSMatthew Barth     {
1440ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1450ce99d8bSMatthew Barth     }
1468600d9a0SMatthew Barth }
1470ce99d8bSMatthew Barth 
1488600d9a0SMatthew Barth void Zone::decTimerExpired()
1498600d9a0SMatthew Barth {
1501ee48f2bSMatthew Barth     // Only decrease speeds when no requested increases exist and
1511ee48f2bSMatthew Barth     // the increase timer is not running (i.e. not in the middle of increasing)
1521ee48f2bSMatthew Barth     if (_incSpeedDelta == 0 && !_incTimer.running())
1530ce99d8bSMatthew Barth     {
1540ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
1550ce99d8bSMatthew Barth         if ((_targetSpeed < _decSpeedDelta) ||
1560ce99d8bSMatthew Barth             (_targetSpeed - _decSpeedDelta < _floorSpeed))
1570ce99d8bSMatthew Barth         {
1580ce99d8bSMatthew Barth             _targetSpeed = _floorSpeed;
1590ce99d8bSMatthew Barth         }
1600ce99d8bSMatthew Barth         else
1610ce99d8bSMatthew Barth         {
1620ce99d8bSMatthew Barth             _targetSpeed = _targetSpeed - _decSpeedDelta;
1630ce99d8bSMatthew Barth         }
1640ce99d8bSMatthew Barth         setSpeed(_targetSpeed);
1650ce99d8bSMatthew Barth     }
1660ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
1670ce99d8bSMatthew Barth     _decSpeedDelta = 0;
1688600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
1690ce99d8bSMatthew Barth }
1700ce99d8bSMatthew Barth 
1711bf0ce4bSMatthew Barth void Zone::initEvents(const ZoneDefinition& def)
1721bf0ce4bSMatthew Barth {
1731bf0ce4bSMatthew Barth     // Setup signal trigger for set speed events
1741bf0ce4bSMatthew Barth     for (auto& event : std::get<setSpeedEventsPos>(def))
1751bf0ce4bSMatthew Barth     {
1761bf0ce4bSMatthew Barth         // Get the current value for each property
1771bf0ce4bSMatthew Barth         for (auto& entry : std::get<groupPos>(event))
1781bf0ce4bSMatthew Barth         {
1791bf0ce4bSMatthew Barth             refreshProperty(_bus,
1801bf0ce4bSMatthew Barth                             entry.first,
1811bf0ce4bSMatthew Barth                             std::get<intfPos>(entry.second),
1821bf0ce4bSMatthew Barth                             std::get<propPos>(entry.second));
1831bf0ce4bSMatthew Barth         }
1841bf0ce4bSMatthew Barth         // Setup signal matches for property change events
1851bf0ce4bSMatthew Barth         for (auto& prop : std::get<propChangeListPos>(event))
1861bf0ce4bSMatthew Barth         {
1871bf0ce4bSMatthew Barth             _signalEvents.emplace_back(
1881bf0ce4bSMatthew Barth                     std::make_unique<EventData>(
1891bf0ce4bSMatthew Barth                             EventData
1901bf0ce4bSMatthew Barth                             {
1911bf0ce4bSMatthew Barth                                 std::get<groupPos>(event),
1921bf0ce4bSMatthew Barth                                 std::get<handlerObjPos>(prop),
1931bf0ce4bSMatthew Barth                                 std::get<actionPos>(event)
1941bf0ce4bSMatthew Barth                             }));
1951bf0ce4bSMatthew Barth             _matches.emplace_back(
1961bf0ce4bSMatthew Barth                     _bus,
1971bf0ce4bSMatthew Barth                     std::get<signaturePos>(prop).c_str(),
1981bf0ce4bSMatthew Barth                     std::bind(std::mem_fn(&Zone::handleEvent),
1991bf0ce4bSMatthew Barth                               this,
2001bf0ce4bSMatthew Barth                               std::placeholders::_1,
2011bf0ce4bSMatthew Barth                               _signalEvents.back().get()));
2021bf0ce4bSMatthew Barth         }
2031bf0ce4bSMatthew Barth         // Run action function for initial event state
2041bf0ce4bSMatthew Barth         std::get<actionPos>(event)(*this,
2051bf0ce4bSMatthew Barth                                    std::get<groupPos>(event));
2061bf0ce4bSMatthew Barth     }
2071bf0ce4bSMatthew Barth }
2081bf0ce4bSMatthew Barth 
2091bf0ce4bSMatthew Barth void Zone::refreshProperty(sdbusplus::bus::bus& bus,
2101bf0ce4bSMatthew Barth                            const std::string& path,
2111bf0ce4bSMatthew Barth                            const std::string& iface,
2121bf0ce4bSMatthew Barth                            const std::string& prop)
2131bf0ce4bSMatthew Barth {
2141bf0ce4bSMatthew Barth     PropertyVariantType property;
2151bf0ce4bSMatthew Barth     getProperty(_bus, path, iface, prop, property);
2161bf0ce4bSMatthew Barth     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
2171bf0ce4bSMatthew Barth }
2181bf0ce4bSMatthew Barth 
219df3e8d67SMatthew Barth void Zone::getProperty(sdbusplus::bus::bus& bus,
220df3e8d67SMatthew Barth                        const std::string& path,
221df3e8d67SMatthew Barth                        const std::string& iface,
222df3e8d67SMatthew Barth                        const std::string& prop,
2239e741ed0SMatthew Barth                        PropertyVariantType& value)
224df3e8d67SMatthew Barth {
225df3e8d67SMatthew Barth     auto serv = phosphor::fan::util::getService(path, iface, bus);
226df3e8d67SMatthew Barth     auto hostCall = bus.new_method_call(serv.c_str(),
227df3e8d67SMatthew Barth                                         path.c_str(),
228df3e8d67SMatthew Barth                                         "org.freedesktop.DBus.Properties",
229df3e8d67SMatthew Barth                                         "Get");
230df3e8d67SMatthew Barth     hostCall.append(iface);
231df3e8d67SMatthew Barth     hostCall.append(prop);
232df3e8d67SMatthew Barth     auto hostResponseMsg = bus.call(hostCall);
233df3e8d67SMatthew Barth     if (hostResponseMsg.is_method_error())
234df3e8d67SMatthew Barth     {
235618027abSDinesh Chinari         log<level::ERR>("Error in host call response for retrieving property");
236618027abSDinesh Chinari         elog<InternalFailure>();
237df3e8d67SMatthew Barth     }
2389e741ed0SMatthew Barth     hostResponseMsg.read(value);
239df3e8d67SMatthew Barth }
240df3e8d67SMatthew Barth 
24138a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
24234f1bda2SMatthew Barth                        const EventData* eventData)
24338a93a8aSMatthew Barth {
24438a93a8aSMatthew Barth     // Handle the callback
24534f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
24617d1fe23SMatthew Barth     // Perform the action
24734f1bda2SMatthew Barth     std::get<eventActionPos>(*eventData)(*this,
24834f1bda2SMatthew Barth                                          std::get<eventGroupPos>(*eventData));
24938a93a8aSMatthew Barth }
25038a93a8aSMatthew Barth 
2517f88fe61SMatt Spinler }
2527f88fe61SMatt Spinler }
2537f88fe61SMatt Spinler }
254