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