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)),
458600d9a0SMatthew Barth     _decTimer(events, [this](){ this->decTimerExpired(); })
467f88fe61SMatt Spinler {
477f88fe61SMatt Spinler     auto& fanDefs = std::get<fanListPos>(def);
487f88fe61SMatt Spinler 
497f88fe61SMatt Spinler     for (auto& def : fanDefs)
507f88fe61SMatt Spinler     {
517f88fe61SMatt Spinler         _fans.emplace_back(std::make_unique<Fan>(bus, def));
527f88fe61SMatt Spinler     }
5338a93a8aSMatthew Barth 
5414184131SMatthew Barth     // Do not enable set speed events when in init mode
5514184131SMatthew Barth     if (mode != Mode::init)
5614184131SMatthew Barth     {
57*1bf0ce4bSMatthew Barth         initEvents(def);
588600d9a0SMatthew Barth         // Start timer for fan speed decreases
598600d9a0SMatthew Barth         if (!_decTimer.running())
608600d9a0SMatthew Barth         {
618600d9a0SMatthew Barth             //TODO Update time value to what's given in zones yaml
628600d9a0SMatthew Barth             _decTimer.start(seconds(30),
638600d9a0SMatthew Barth                             phosphor::fan::util::Timer::TimerType::repeating);
648600d9a0SMatthew Barth         }
657f88fe61SMatt Spinler     }
6614184131SMatthew Barth }
677f88fe61SMatt Spinler 
687f88fe61SMatt Spinler 
697f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
707f88fe61SMatt Spinler {
717f88fe61SMatt Spinler     for (auto& fan : _fans)
727f88fe61SMatt Spinler     {
737f88fe61SMatt Spinler         fan->setSpeed(speed);
747f88fe61SMatt Spinler     }
757f88fe61SMatt Spinler }
767f88fe61SMatt Spinler 
77861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
78861d77c3SMatthew Barth {
79861d77c3SMatthew Barth     _active[group] = isActiveAllow;
80861d77c3SMatthew Barth     if (!isActiveAllow)
81861d77c3SMatthew Barth     {
82861d77c3SMatthew Barth         _isActive = false;
83861d77c3SMatthew Barth     }
84861d77c3SMatthew Barth     else
85861d77c3SMatthew Barth     {
86861d77c3SMatthew Barth         // Check all entries are set to allow control active
87861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
88861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
89861d77c3SMatthew Barth                                 _active.end(),
90861d77c3SMatthew Barth                                 actPred);
91861d77c3SMatthew Barth     }
92861d77c3SMatthew Barth }
93861d77c3SMatthew Barth 
94240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
95240397b9SMatthew Barth {
96240397b9SMatthew Barth     // Only increase speed when delta is higher than
97240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
98240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
99240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
100240397b9SMatthew Barth     {
101240397b9SMatthew Barth         _targetSpeed = (targetDelta - _incSpeedDelta) + _targetSpeed;
102240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
103240397b9SMatthew Barth         //TODO openbmc/openbmc#1625 Cancel current timer countdown
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         }
114240397b9SMatthew Barth 
115240397b9SMatthew Barth         setSpeed(_targetSpeed);
116240397b9SMatthew Barth         //TODO openbmc/openbmc#1625 Start timer countdown for fan speed increase
117240397b9SMatthew Barth     }
118240397b9SMatthew Barth     //TODO openbmc/openbmc#1625 Clear increase delta when timer expires
119240397b9SMatthew Barth     _incSpeedDelta = 0;
120240397b9SMatthew Barth }
121240397b9SMatthew Barth 
1220ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1230ce99d8bSMatthew Barth {
1240ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1250ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1260ce99d8bSMatthew Barth     {
1270ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1280ce99d8bSMatthew Barth     }
1298600d9a0SMatthew Barth }
1300ce99d8bSMatthew Barth 
1318600d9a0SMatthew Barth void Zone::decTimerExpired()
1328600d9a0SMatthew Barth {
1330ce99d8bSMatthew Barth     // Only decrease speeds when no requested increases exist
1348600d9a0SMatthew Barth     //TODO Add increase timer not running (i.e. not in the middle of increasing)
1350ce99d8bSMatthew Barth     if (_incSpeedDelta == 0)
1360ce99d8bSMatthew Barth     {
1370ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
1380ce99d8bSMatthew Barth         if ((_targetSpeed < _decSpeedDelta) ||
1390ce99d8bSMatthew Barth             (_targetSpeed - _decSpeedDelta < _floorSpeed))
1400ce99d8bSMatthew Barth         {
1410ce99d8bSMatthew Barth             _targetSpeed = _floorSpeed;
1420ce99d8bSMatthew Barth         }
1430ce99d8bSMatthew Barth         else
1440ce99d8bSMatthew Barth         {
1450ce99d8bSMatthew Barth             _targetSpeed = _targetSpeed - _decSpeedDelta;
1460ce99d8bSMatthew Barth         }
1470ce99d8bSMatthew Barth         setSpeed(_targetSpeed);
1480ce99d8bSMatthew Barth     }
1490ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
1500ce99d8bSMatthew Barth     _decSpeedDelta = 0;
1518600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
1520ce99d8bSMatthew Barth }
1530ce99d8bSMatthew Barth 
154*1bf0ce4bSMatthew Barth void Zone::initEvents(const ZoneDefinition& def)
155*1bf0ce4bSMatthew Barth {
156*1bf0ce4bSMatthew Barth     // Setup signal trigger for set speed events
157*1bf0ce4bSMatthew Barth     for (auto& event : std::get<setSpeedEventsPos>(def))
158*1bf0ce4bSMatthew Barth     {
159*1bf0ce4bSMatthew Barth         // Get the current value for each property
160*1bf0ce4bSMatthew Barth         for (auto& entry : std::get<groupPos>(event))
161*1bf0ce4bSMatthew Barth         {
162*1bf0ce4bSMatthew Barth             refreshProperty(_bus,
163*1bf0ce4bSMatthew Barth                             entry.first,
164*1bf0ce4bSMatthew Barth                             std::get<intfPos>(entry.second),
165*1bf0ce4bSMatthew Barth                             std::get<propPos>(entry.second));
166*1bf0ce4bSMatthew Barth         }
167*1bf0ce4bSMatthew Barth         // Setup signal matches for property change events
168*1bf0ce4bSMatthew Barth         for (auto& prop : std::get<propChangeListPos>(event))
169*1bf0ce4bSMatthew Barth         {
170*1bf0ce4bSMatthew Barth             _signalEvents.emplace_back(
171*1bf0ce4bSMatthew Barth                     std::make_unique<EventData>(
172*1bf0ce4bSMatthew Barth                             EventData
173*1bf0ce4bSMatthew Barth                             {
174*1bf0ce4bSMatthew Barth                                 std::get<groupPos>(event),
175*1bf0ce4bSMatthew Barth                                 std::get<handlerObjPos>(prop),
176*1bf0ce4bSMatthew Barth                                 std::get<actionPos>(event)
177*1bf0ce4bSMatthew Barth                             }));
178*1bf0ce4bSMatthew Barth             _matches.emplace_back(
179*1bf0ce4bSMatthew Barth                     _bus,
180*1bf0ce4bSMatthew Barth                     std::get<signaturePos>(prop).c_str(),
181*1bf0ce4bSMatthew Barth                     std::bind(std::mem_fn(&Zone::handleEvent),
182*1bf0ce4bSMatthew Barth                               this,
183*1bf0ce4bSMatthew Barth                               std::placeholders::_1,
184*1bf0ce4bSMatthew Barth                               _signalEvents.back().get()));
185*1bf0ce4bSMatthew Barth         }
186*1bf0ce4bSMatthew Barth         // Run action function for initial event state
187*1bf0ce4bSMatthew Barth         std::get<actionPos>(event)(*this,
188*1bf0ce4bSMatthew Barth                                    std::get<groupPos>(event));
189*1bf0ce4bSMatthew Barth     }
190*1bf0ce4bSMatthew Barth }
191*1bf0ce4bSMatthew Barth 
192*1bf0ce4bSMatthew Barth void Zone::refreshProperty(sdbusplus::bus::bus& bus,
193*1bf0ce4bSMatthew Barth                            const std::string& path,
194*1bf0ce4bSMatthew Barth                            const std::string& iface,
195*1bf0ce4bSMatthew Barth                            const std::string& prop)
196*1bf0ce4bSMatthew Barth {
197*1bf0ce4bSMatthew Barth     PropertyVariantType property;
198*1bf0ce4bSMatthew Barth     getProperty(_bus, path, iface, prop, property);
199*1bf0ce4bSMatthew Barth     setPropertyValue(path.c_str(), iface.c_str(), prop.c_str(), property);
200*1bf0ce4bSMatthew Barth }
201*1bf0ce4bSMatthew Barth 
202df3e8d67SMatthew Barth void Zone::getProperty(sdbusplus::bus::bus& bus,
203df3e8d67SMatthew Barth                        const std::string& path,
204df3e8d67SMatthew Barth                        const std::string& iface,
205df3e8d67SMatthew Barth                        const std::string& prop,
2069e741ed0SMatthew Barth                        PropertyVariantType& value)
207df3e8d67SMatthew Barth {
208df3e8d67SMatthew Barth     auto serv = phosphor::fan::util::getService(path, iface, bus);
209df3e8d67SMatthew Barth     auto hostCall = bus.new_method_call(serv.c_str(),
210df3e8d67SMatthew Barth                                         path.c_str(),
211df3e8d67SMatthew Barth                                         "org.freedesktop.DBus.Properties",
212df3e8d67SMatthew Barth                                         "Get");
213df3e8d67SMatthew Barth     hostCall.append(iface);
214df3e8d67SMatthew Barth     hostCall.append(prop);
215df3e8d67SMatthew Barth     auto hostResponseMsg = bus.call(hostCall);
216df3e8d67SMatthew Barth     if (hostResponseMsg.is_method_error())
217df3e8d67SMatthew Barth     {
218618027abSDinesh Chinari         log<level::ERR>("Error in host call response for retrieving property");
219618027abSDinesh Chinari         elog<InternalFailure>();
220df3e8d67SMatthew Barth     }
2219e741ed0SMatthew Barth     hostResponseMsg.read(value);
222df3e8d67SMatthew Barth }
223df3e8d67SMatthew Barth 
22438a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
22534f1bda2SMatthew Barth                        const EventData* eventData)
22638a93a8aSMatthew Barth {
22738a93a8aSMatthew Barth     // Handle the callback
22834f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
22917d1fe23SMatthew Barth     // Perform the action
23034f1bda2SMatthew Barth     std::get<eventActionPos>(*eventData)(*this,
23134f1bda2SMatthew Barth                                          std::get<eventGroupPos>(*eventData));
23238a93a8aSMatthew Barth }
23338a93a8aSMatthew Barth 
2347f88fe61SMatt Spinler }
2357f88fe61SMatt Spinler }
2367f88fe61SMatt Spinler }
237