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)),
45a956184bSMatthew Barth     _incDelay(std::get<incDelayPos>(def)),
46a956184bSMatthew 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     {
60ccc7770eSMatthew Barth         // Setup signal trigger for set speed events
61ccc7770eSMatthew Barth         for (auto& event : std::get<setSpeedEventsPos>(def))
62ccc7770eSMatthew Barth         {
63ccc7770eSMatthew Barth             initEvent(event);
64ccc7770eSMatthew Barth         }
658600d9a0SMatthew Barth         // Start timer for fan speed decreases
66a956184bSMatthew Barth         if (!_decTimer.running() && _decInterval != seconds::zero())
678600d9a0SMatthew Barth         {
68a956184bSMatthew Barth             _decTimer.start(_decInterval,
698600d9a0SMatthew Barth                             phosphor::fan::util::Timer::TimerType::repeating);
708600d9a0SMatthew Barth         }
717f88fe61SMatt Spinler     }
7214184131SMatthew Barth }
737f88fe61SMatt Spinler 
747f88fe61SMatt Spinler 
757f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
767f88fe61SMatt Spinler {
777f88fe61SMatt Spinler     for (auto& fan : _fans)
787f88fe61SMatt Spinler     {
797f88fe61SMatt Spinler         fan->setSpeed(speed);
807f88fe61SMatt Spinler     }
817f88fe61SMatt Spinler }
827f88fe61SMatt Spinler 
83861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
84861d77c3SMatthew Barth {
85861d77c3SMatthew Barth     _active[group] = isActiveAllow;
86861d77c3SMatthew Barth     if (!isActiveAllow)
87861d77c3SMatthew Barth     {
88861d77c3SMatthew Barth         _isActive = false;
89861d77c3SMatthew Barth     }
90861d77c3SMatthew Barth     else
91861d77c3SMatthew Barth     {
92861d77c3SMatthew Barth         // Check all entries are set to allow control active
93861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
94861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
95861d77c3SMatthew Barth                                 _active.end(),
96861d77c3SMatthew Barth                                 actPred);
97861d77c3SMatthew Barth     }
98861d77c3SMatthew Barth }
99861d77c3SMatthew Barth 
100b4a7cb99SMatthew Barth void Zone::setFloor(uint64_t speed)
101b4a7cb99SMatthew Barth {
102b4a7cb99SMatthew Barth     _floorSpeed = speed;
103b4a7cb99SMatthew Barth     // Floor speed above target, update target to floor speed
104b4a7cb99SMatthew Barth     if (_targetSpeed < _floorSpeed)
105b4a7cb99SMatthew Barth     {
106b4a7cb99SMatthew Barth         requestSpeedIncrease(_floorSpeed - _targetSpeed);
107b4a7cb99SMatthew Barth     }
108b4a7cb99SMatthew Barth }
109b4a7cb99SMatthew Barth 
110240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
111240397b9SMatthew Barth {
112240397b9SMatthew Barth     // Only increase speed when delta is higher than
113240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
114240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
115240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
116240397b9SMatthew Barth     {
117240397b9SMatthew Barth         _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
118240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
119240397b9SMatthew Barth         // Target speed can not go above a defined ceiling speed
120240397b9SMatthew Barth         if (_targetSpeed > _ceilingSpeed)
121240397b9SMatthew Barth         {
122240397b9SMatthew Barth             _targetSpeed = _ceilingSpeed;
123240397b9SMatthew Barth         }
1241ee48f2bSMatthew Barth         // Cancel current timer countdown
1251ee48f2bSMatthew Barth         if (_incTimer.running())
1261ee48f2bSMatthew Barth         {
1271ee48f2bSMatthew Barth             _incTimer.stop();
128240397b9SMatthew Barth         }
1291ee48f2bSMatthew Barth         setSpeed(_targetSpeed);
1301ee48f2bSMatthew Barth         // Start timer countdown for fan speed increase
131a956184bSMatthew Barth         _incTimer.start(_incDelay,
1321ee48f2bSMatthew Barth                         phosphor::fan::util::Timer::TimerType::oneshot);
1331ee48f2bSMatthew Barth     }
1341ee48f2bSMatthew Barth }
1351ee48f2bSMatthew Barth 
1361ee48f2bSMatthew Barth void Zone::incTimerExpired()
1371ee48f2bSMatthew Barth {
1381ee48f2bSMatthew Barth     // Clear increase delta when timer expires allowing additional speed
1391ee48f2bSMatthew Barth     // increase requests or speed decreases to occur
140240397b9SMatthew Barth     _incSpeedDelta = 0;
141240397b9SMatthew Barth }
142240397b9SMatthew Barth 
1430ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1440ce99d8bSMatthew Barth {
1450ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1460ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1470ce99d8bSMatthew Barth     {
1480ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1490ce99d8bSMatthew Barth     }
1508600d9a0SMatthew Barth }
1510ce99d8bSMatthew Barth 
1528600d9a0SMatthew Barth void Zone::decTimerExpired()
1538600d9a0SMatthew Barth {
1541ee48f2bSMatthew Barth     // Only decrease speeds when no requested increases exist and
1551ee48f2bSMatthew Barth     // the increase timer is not running (i.e. not in the middle of increasing)
1561ee48f2bSMatthew Barth     if (_incSpeedDelta == 0 && !_incTimer.running())
1570ce99d8bSMatthew Barth     {
1580ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
1590ce99d8bSMatthew Barth         if ((_targetSpeed < _decSpeedDelta) ||
1600ce99d8bSMatthew Barth             (_targetSpeed - _decSpeedDelta < _floorSpeed))
1610ce99d8bSMatthew Barth         {
1620ce99d8bSMatthew Barth             _targetSpeed = _floorSpeed;
1630ce99d8bSMatthew Barth         }
1640ce99d8bSMatthew Barth         else
1650ce99d8bSMatthew Barth         {
1660ce99d8bSMatthew Barth             _targetSpeed = _targetSpeed - _decSpeedDelta;
1670ce99d8bSMatthew Barth         }
1680ce99d8bSMatthew Barth         setSpeed(_targetSpeed);
1690ce99d8bSMatthew Barth     }
1700ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
1710ce99d8bSMatthew Barth     _decSpeedDelta = 0;
1728600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
1730ce99d8bSMatthew Barth }
1740ce99d8bSMatthew Barth 
175ccc7770eSMatthew Barth void Zone::initEvent(const SetSpeedEvent& event)
1761bf0ce4bSMatthew Barth {
1771bf0ce4bSMatthew Barth     // Get the current value for each property
178604329efSMatthew Barth     for (auto& group : std::get<groupPos>(event))
179604329efSMatthew Barth     {
180604329efSMatthew Barth         try
1811bf0ce4bSMatthew Barth         {
1821bf0ce4bSMatthew Barth             refreshProperty(_bus,
183604329efSMatthew Barth                             group.first,
184604329efSMatthew Barth                             std::get<intfPos>(group.second),
185604329efSMatthew Barth                             std::get<propPos>(group.second));
186604329efSMatthew Barth         }
187604329efSMatthew Barth         catch (const InternalFailure& ife)
188604329efSMatthew Barth         {
189604329efSMatthew Barth             log<level::ERR>(
190604329efSMatthew Barth                 "Unable to find property: ",
191604329efSMatthew Barth                 entry("PATH=%s", group.first.c_str()),
192604329efSMatthew Barth                 entry("INTERFACE=%s", std::get<intfPos>(group.second).c_str()),
193604329efSMatthew Barth                 entry("PROPERTY=%s", std::get<propPos>(group.second).c_str()));
194604329efSMatthew Barth         }
1951bf0ce4bSMatthew Barth     }
1961bf0ce4bSMatthew Barth     // Setup signal matches for property change events
1971bf0ce4bSMatthew Barth     for (auto& prop : std::get<propChangeListPos>(event))
1981bf0ce4bSMatthew Barth     {
199*f6b76d8eSMatthew Barth         std::unique_ptr<EventData> eventData =
2001bf0ce4bSMatthew Barth             std::make_unique<EventData>(
2011bf0ce4bSMatthew Barth                 EventData
2021bf0ce4bSMatthew Barth                 {
2031bf0ce4bSMatthew Barth                     std::get<groupPos>(event),
2041bf0ce4bSMatthew Barth                     std::get<handlerObjPos>(prop),
2051bf0ce4bSMatthew Barth                     std::get<actionPos>(event)
206*f6b76d8eSMatthew Barth                 }
207*f6b76d8eSMatthew Barth             );
208*f6b76d8eSMatthew Barth         std::unique_ptr<sdbusplus::server::match::match> match =
209*f6b76d8eSMatthew Barth             std::make_unique<sdbusplus::server::match::match>(
2101bf0ce4bSMatthew Barth                 _bus,
2111bf0ce4bSMatthew Barth                 std::get<signaturePos>(prop).c_str(),
2121bf0ce4bSMatthew Barth                 std::bind(std::mem_fn(&Zone::handleEvent),
2131bf0ce4bSMatthew Barth                           this,
2141bf0ce4bSMatthew Barth                           std::placeholders::_1,
215*f6b76d8eSMatthew Barth                           eventData.get())
216*f6b76d8eSMatthew Barth             );
217*f6b76d8eSMatthew Barth         _signalEvents.emplace_back(std::move(eventData), std::move(match));
2181bf0ce4bSMatthew Barth     }
2191bf0ce4bSMatthew Barth     // Run action function for initial event state
2201bf0ce4bSMatthew Barth     std::get<actionPos>(event)(*this,
2211bf0ce4bSMatthew Barth                                std::get<groupPos>(event));
2221bf0ce4bSMatthew Barth }
2231bf0ce4bSMatthew Barth 
224*f6b76d8eSMatthew Barth void Zone::removeEvent(const SetSpeedEvent& event)
225*f6b76d8eSMatthew Barth {
226*f6b76d8eSMatthew Barth     // Find the signal event to be removed
227*f6b76d8eSMatthew Barth     auto it = std::find_if(
228*f6b76d8eSMatthew Barth         _signalEvents.begin(),
229*f6b76d8eSMatthew Barth         _signalEvents.end(),
230*f6b76d8eSMatthew Barth         [&event](auto const& se)
231*f6b76d8eSMatthew Barth         {
232*f6b76d8eSMatthew Barth             auto seEventData = *std::get<signalEventDataPos>(se);
233*f6b76d8eSMatthew Barth             // TODO Use the action function target for comparison
234*f6b76d8eSMatthew Barth             return
235*f6b76d8eSMatthew Barth             (
236*f6b76d8eSMatthew Barth                 std::get<eventGroupPos>(seEventData) ==
237*f6b76d8eSMatthew Barth                     std::get<groupPos>(event) &&
238*f6b76d8eSMatthew Barth                 std::get<eventActionPos>(seEventData).target_type().name() ==
239*f6b76d8eSMatthew Barth                     std::get<actionPos>(event).target_type().name()
240*f6b76d8eSMatthew Barth             );
241*f6b76d8eSMatthew Barth         });
242*f6b76d8eSMatthew Barth     if (it != std::end(_signalEvents))
243*f6b76d8eSMatthew Barth     {
244*f6b76d8eSMatthew Barth         std::get<signalEventDataPos>(*it).reset();
245*f6b76d8eSMatthew Barth         std::get<signalMatchPos>(*it).reset();
246*f6b76d8eSMatthew Barth         _signalEvents.erase(it);
247*f6b76d8eSMatthew Barth     }
248*f6b76d8eSMatthew Barth }
249*f6b76d8eSMatthew Barth 
2501bf0ce4bSMatthew Barth void Zone::refreshProperty(sdbusplus::bus::bus& bus,
2511bf0ce4bSMatthew Barth                            const std::string& path,
2521bf0ce4bSMatthew Barth                            const std::string& iface,
2531bf0ce4bSMatthew Barth                            const std::string& prop)
2541bf0ce4bSMatthew Barth {
2551bf0ce4bSMatthew Barth     PropertyVariantType property;
2561bf0ce4bSMatthew Barth     getProperty(_bus, path, iface, prop, property);
2571bf0ce4bSMatthew Barth     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
2581bf0ce4bSMatthew Barth }
2591bf0ce4bSMatthew Barth 
260df3e8d67SMatthew Barth void Zone::getProperty(sdbusplus::bus::bus& bus,
261df3e8d67SMatthew Barth                        const std::string& path,
262df3e8d67SMatthew Barth                        const std::string& iface,
263df3e8d67SMatthew Barth                        const std::string& prop,
2649e741ed0SMatthew Barth                        PropertyVariantType& value)
265df3e8d67SMatthew Barth {
266df3e8d67SMatthew Barth     auto serv = phosphor::fan::util::getService(path, iface, bus);
267df3e8d67SMatthew Barth     auto hostCall = bus.new_method_call(serv.c_str(),
268df3e8d67SMatthew Barth                                         path.c_str(),
269df3e8d67SMatthew Barth                                         "org.freedesktop.DBus.Properties",
270df3e8d67SMatthew Barth                                         "Get");
271df3e8d67SMatthew Barth     hostCall.append(iface);
272df3e8d67SMatthew Barth     hostCall.append(prop);
273df3e8d67SMatthew Barth     auto hostResponseMsg = bus.call(hostCall);
274df3e8d67SMatthew Barth     if (hostResponseMsg.is_method_error())
275df3e8d67SMatthew Barth     {
276618027abSDinesh Chinari         log<level::ERR>("Error in host call response for retrieving property");
277618027abSDinesh Chinari         elog<InternalFailure>();
278df3e8d67SMatthew Barth     }
2799e741ed0SMatthew Barth     hostResponseMsg.read(value);
280df3e8d67SMatthew Barth }
281df3e8d67SMatthew Barth 
28238a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
28334f1bda2SMatthew Barth                        const EventData* eventData)
28438a93a8aSMatthew Barth {
28538a93a8aSMatthew Barth     // Handle the callback
28634f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
28717d1fe23SMatthew Barth     // Perform the action
28834f1bda2SMatthew Barth     std::get<eventActionPos>(*eventData)(*this,
28934f1bda2SMatthew Barth                                          std::get<eventGroupPos>(*eventData));
29038a93a8aSMatthew Barth }
29138a93a8aSMatthew Barth 
2927f88fe61SMatt Spinler }
2937f88fe61SMatt Spinler }
2947f88fe61SMatt Spinler }
295