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*1ee48f2bSMatthew Barth     _incTimer(events, [this](){ this->incTimerExpired(); }),
468600d9a0SMatthew Barth     _decTimer(events, [this](){ this->decTimerExpired(); })
477f88fe61SMatt Spinler {
487f88fe61SMatt Spinler     auto& fanDefs = std::get<fanListPos>(def);
497f88fe61SMatt Spinler 
507f88fe61SMatt Spinler     for (auto& def : fanDefs)
517f88fe61SMatt Spinler     {
527f88fe61SMatt Spinler         _fans.emplace_back(std::make_unique<Fan>(bus, def));
537f88fe61SMatt Spinler     }
5438a93a8aSMatthew Barth 
5514184131SMatthew Barth     // Do not enable set speed events when in init mode
5614184131SMatthew Barth     if (mode != Mode::init)
5714184131SMatthew Barth     {
581bf0ce4bSMatthew Barth         initEvents(def);
598600d9a0SMatthew Barth         // Start timer for fan speed decreases
608600d9a0SMatthew Barth         if (!_decTimer.running())
618600d9a0SMatthew Barth         {
628600d9a0SMatthew Barth             //TODO Update time value to what's given in zones yaml
638600d9a0SMatthew Barth             _decTimer.start(seconds(30),
648600d9a0SMatthew Barth                             phosphor::fan::util::Timer::TimerType::repeating);
658600d9a0SMatthew Barth         }
667f88fe61SMatt Spinler     }
6714184131SMatthew Barth }
687f88fe61SMatt Spinler 
697f88fe61SMatt Spinler 
707f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
717f88fe61SMatt Spinler {
727f88fe61SMatt Spinler     for (auto& fan : _fans)
737f88fe61SMatt Spinler     {
747f88fe61SMatt Spinler         fan->setSpeed(speed);
757f88fe61SMatt Spinler     }
767f88fe61SMatt Spinler }
777f88fe61SMatt Spinler 
78861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
79861d77c3SMatthew Barth {
80861d77c3SMatthew Barth     _active[group] = isActiveAllow;
81861d77c3SMatthew Barth     if (!isActiveAllow)
82861d77c3SMatthew Barth     {
83861d77c3SMatthew Barth         _isActive = false;
84861d77c3SMatthew Barth     }
85861d77c3SMatthew Barth     else
86861d77c3SMatthew Barth     {
87861d77c3SMatthew Barth         // Check all entries are set to allow control active
88861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
89861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
90861d77c3SMatthew Barth                                 _active.end(),
91861d77c3SMatthew Barth                                 actPred);
92861d77c3SMatthew Barth     }
93861d77c3SMatthew Barth }
94861d77c3SMatthew Barth 
95240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
96240397b9SMatthew Barth {
97240397b9SMatthew Barth     // Only increase speed when delta is higher than
98240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
99240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
100240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
101240397b9SMatthew Barth     {
102240397b9SMatthew Barth         _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
103240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
104240397b9SMatthew Barth         //TODO Floor speed above target, update target to floor speed
105240397b9SMatthew Barth         if (_targetSpeed < _floorSpeed)
106240397b9SMatthew Barth         {
107240397b9SMatthew Barth             _targetSpeed = _floorSpeed;
108240397b9SMatthew Barth         }
109240397b9SMatthew Barth         // Target speed can not go above a defined ceiling speed
110240397b9SMatthew Barth         if (_targetSpeed > _ceilingSpeed)
111240397b9SMatthew Barth         {
112240397b9SMatthew Barth             _targetSpeed = _ceilingSpeed;
113240397b9SMatthew Barth         }
114*1ee48f2bSMatthew Barth         // Cancel current timer countdown
115*1ee48f2bSMatthew Barth         if (_incTimer.running())
116*1ee48f2bSMatthew Barth         {
117*1ee48f2bSMatthew Barth             _incTimer.stop();
118240397b9SMatthew Barth         }
119*1ee48f2bSMatthew Barth         setSpeed(_targetSpeed);
120*1ee48f2bSMatthew Barth         // Start timer countdown for fan speed increase
121*1ee48f2bSMatthew Barth         //TODO Update time value to what's given in zones yaml
122*1ee48f2bSMatthew Barth         _incTimer.start(seconds(5),
123*1ee48f2bSMatthew Barth                         phosphor::fan::util::Timer::TimerType::oneshot);
124*1ee48f2bSMatthew Barth     }
125*1ee48f2bSMatthew Barth }
126*1ee48f2bSMatthew Barth 
127*1ee48f2bSMatthew Barth void Zone::incTimerExpired()
128*1ee48f2bSMatthew Barth {
129*1ee48f2bSMatthew Barth     // Clear increase delta when timer expires allowing additional speed
130*1ee48f2bSMatthew Barth     // increase requests or speed decreases to occur
131240397b9SMatthew Barth     _incSpeedDelta = 0;
132240397b9SMatthew Barth }
133240397b9SMatthew Barth 
1340ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1350ce99d8bSMatthew Barth {
1360ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1370ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1380ce99d8bSMatthew Barth     {
1390ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1400ce99d8bSMatthew Barth     }
1418600d9a0SMatthew Barth }
1420ce99d8bSMatthew Barth 
1438600d9a0SMatthew Barth void Zone::decTimerExpired()
1448600d9a0SMatthew Barth {
145*1ee48f2bSMatthew Barth     // Only decrease speeds when no requested increases exist and
146*1ee48f2bSMatthew Barth     // the increase timer is not running (i.e. not in the middle of increasing)
147*1ee48f2bSMatthew Barth     if (_incSpeedDelta == 0 && !_incTimer.running())
1480ce99d8bSMatthew Barth     {
1490ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
1500ce99d8bSMatthew Barth         if ((_targetSpeed < _decSpeedDelta) ||
1510ce99d8bSMatthew Barth             (_targetSpeed - _decSpeedDelta < _floorSpeed))
1520ce99d8bSMatthew Barth         {
1530ce99d8bSMatthew Barth             _targetSpeed = _floorSpeed;
1540ce99d8bSMatthew Barth         }
1550ce99d8bSMatthew Barth         else
1560ce99d8bSMatthew Barth         {
1570ce99d8bSMatthew Barth             _targetSpeed = _targetSpeed - _decSpeedDelta;
1580ce99d8bSMatthew Barth         }
1590ce99d8bSMatthew Barth         setSpeed(_targetSpeed);
1600ce99d8bSMatthew Barth     }
1610ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
1620ce99d8bSMatthew Barth     _decSpeedDelta = 0;
1638600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
1640ce99d8bSMatthew Barth }
1650ce99d8bSMatthew Barth 
1661bf0ce4bSMatthew Barth void Zone::initEvents(const ZoneDefinition& def)
1671bf0ce4bSMatthew Barth {
1681bf0ce4bSMatthew Barth     // Setup signal trigger for set speed events
1691bf0ce4bSMatthew Barth     for (auto& event : std::get<setSpeedEventsPos>(def))
1701bf0ce4bSMatthew Barth     {
1711bf0ce4bSMatthew Barth         // Get the current value for each property
1721bf0ce4bSMatthew Barth         for (auto& entry : std::get<groupPos>(event))
1731bf0ce4bSMatthew Barth         {
1741bf0ce4bSMatthew Barth             refreshProperty(_bus,
1751bf0ce4bSMatthew Barth                             entry.first,
1761bf0ce4bSMatthew Barth                             std::get<intfPos>(entry.second),
1771bf0ce4bSMatthew Barth                             std::get<propPos>(entry.second));
1781bf0ce4bSMatthew Barth         }
1791bf0ce4bSMatthew Barth         // Setup signal matches for property change events
1801bf0ce4bSMatthew Barth         for (auto& prop : std::get<propChangeListPos>(event))
1811bf0ce4bSMatthew Barth         {
1821bf0ce4bSMatthew Barth             _signalEvents.emplace_back(
1831bf0ce4bSMatthew Barth                     std::make_unique<EventData>(
1841bf0ce4bSMatthew Barth                             EventData
1851bf0ce4bSMatthew Barth                             {
1861bf0ce4bSMatthew Barth                                 std::get<groupPos>(event),
1871bf0ce4bSMatthew Barth                                 std::get<handlerObjPos>(prop),
1881bf0ce4bSMatthew Barth                                 std::get<actionPos>(event)
1891bf0ce4bSMatthew Barth                             }));
1901bf0ce4bSMatthew Barth             _matches.emplace_back(
1911bf0ce4bSMatthew Barth                     _bus,
1921bf0ce4bSMatthew Barth                     std::get<signaturePos>(prop).c_str(),
1931bf0ce4bSMatthew Barth                     std::bind(std::mem_fn(&Zone::handleEvent),
1941bf0ce4bSMatthew Barth                               this,
1951bf0ce4bSMatthew Barth                               std::placeholders::_1,
1961bf0ce4bSMatthew Barth                               _signalEvents.back().get()));
1971bf0ce4bSMatthew Barth         }
1981bf0ce4bSMatthew Barth         // Run action function for initial event state
1991bf0ce4bSMatthew Barth         std::get<actionPos>(event)(*this,
2001bf0ce4bSMatthew Barth                                    std::get<groupPos>(event));
2011bf0ce4bSMatthew Barth     }
2021bf0ce4bSMatthew Barth }
2031bf0ce4bSMatthew Barth 
2041bf0ce4bSMatthew Barth void Zone::refreshProperty(sdbusplus::bus::bus& bus,
2051bf0ce4bSMatthew Barth                            const std::string& path,
2061bf0ce4bSMatthew Barth                            const std::string& iface,
2071bf0ce4bSMatthew Barth                            const std::string& prop)
2081bf0ce4bSMatthew Barth {
2091bf0ce4bSMatthew Barth     PropertyVariantType property;
2101bf0ce4bSMatthew Barth     getProperty(_bus, path, iface, prop, property);
2111bf0ce4bSMatthew Barth     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
2121bf0ce4bSMatthew Barth }
2131bf0ce4bSMatthew Barth 
214df3e8d67SMatthew Barth void Zone::getProperty(sdbusplus::bus::bus& bus,
215df3e8d67SMatthew Barth                        const std::string& path,
216df3e8d67SMatthew Barth                        const std::string& iface,
217df3e8d67SMatthew Barth                        const std::string& prop,
2189e741ed0SMatthew Barth                        PropertyVariantType& value)
219df3e8d67SMatthew Barth {
220df3e8d67SMatthew Barth     auto serv = phosphor::fan::util::getService(path, iface, bus);
221df3e8d67SMatthew Barth     auto hostCall = bus.new_method_call(serv.c_str(),
222df3e8d67SMatthew Barth                                         path.c_str(),
223df3e8d67SMatthew Barth                                         "org.freedesktop.DBus.Properties",
224df3e8d67SMatthew Barth                                         "Get");
225df3e8d67SMatthew Barth     hostCall.append(iface);
226df3e8d67SMatthew Barth     hostCall.append(prop);
227df3e8d67SMatthew Barth     auto hostResponseMsg = bus.call(hostCall);
228df3e8d67SMatthew Barth     if (hostResponseMsg.is_method_error())
229df3e8d67SMatthew Barth     {
230618027abSDinesh Chinari         log<level::ERR>("Error in host call response for retrieving property");
231618027abSDinesh Chinari         elog<InternalFailure>();
232df3e8d67SMatthew Barth     }
2339e741ed0SMatthew Barth     hostResponseMsg.read(value);
234df3e8d67SMatthew Barth }
235df3e8d67SMatthew Barth 
23638a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
23734f1bda2SMatthew Barth                        const EventData* eventData)
23838a93a8aSMatthew Barth {
23938a93a8aSMatthew Barth     // Handle the callback
24034f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
24117d1fe23SMatthew Barth     // Perform the action
24234f1bda2SMatthew Barth     std::get<eventActionPos>(*eventData)(*this,
24334f1bda2SMatthew Barth                                          std::get<eventGroupPos>(*eventData));
24438a93a8aSMatthew Barth }
24538a93a8aSMatthew Barth 
2467f88fe61SMatt Spinler }
2477f88fe61SMatt Spinler }
2487f88fe61SMatt Spinler }
249