1 #include <powercap.hpp> 2 #include <phosphor-logging/log.hpp> 3 4 namespace open_power 5 { 6 namespace occ 7 { 8 namespace powercap 9 { 10 11 constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap"; 12 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap"; 13 14 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 15 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 16 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 17 18 constexpr auto POWER_CAP_PROP = "PowerCap"; 19 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable"; 20 21 using namespace phosphor::logging; 22 23 std::string PowerCap::getService(std::string path, 24 std::string interface) 25 { 26 auto mapper = bus.new_method_call(MAPPER_BUSNAME, 27 MAPPER_PATH, 28 MAPPER_INTERFACE, 29 "GetObject"); 30 31 mapper.append(path, std::vector<std::string>({interface})); 32 auto mapperResponseMsg = bus.call(mapper); 33 34 if (mapperResponseMsg.is_method_error()) 35 { 36 log<level::ERR>("Error in mapper call", 37 entry("PATH=%s", path.c_str()), 38 entry("INTERFACE=%s", interface.c_str())); 39 // TODO openbmc/openbmc#851 - Once available, throw returned error 40 throw std::runtime_error("Error in mapper call"); 41 } 42 43 std::map<std::string, std::vector<std::string>> mapperResponse; 44 mapperResponseMsg.read(mapperResponse); 45 if (mapperResponse.empty()) 46 { 47 log<level::ERR>("Error reading mapper response", 48 entry("PATH=%s", path.c_str()), 49 entry("INTERFACE=%s", interface.c_str())); 50 // TODO openbmc/openbmc#1712 - Handle empty mapper resp. consistently 51 throw std::runtime_error("Error reading mapper response"); 52 } 53 54 return mapperResponse.begin()->first; 55 } 56 57 uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled) 58 { 59 if (!pcapEnabled) 60 { 61 // Pcap disabled, return 0 to indicate disabled 62 return 0; 63 } 64 65 // If pcap is not disabled then just return the pcap with the derating 66 // factor applied. 67 return( (static_cast<uint64_t>(pcap) * PS_DERATING_FACTOR) /100); 68 } 69 70 uint32_t PowerCap::getPcap() 71 { 72 auto settingService = getService(PCAP_PATH,PCAP_INTERFACE); 73 74 auto method = this->bus.new_method_call(settingService.c_str(), 75 PCAP_PATH, 76 "org.freedesktop.DBus.Properties", 77 "Get"); 78 79 method.append(PCAP_INTERFACE, POWER_CAP_PROP); 80 auto reply = this->bus.call(method); 81 82 if (reply.is_method_error()) 83 { 84 log<level::ERR>("Error in getPcap prop"); 85 return 0; 86 } 87 sdbusplus::message::variant<uint32_t> pcap; 88 reply.read(pcap); 89 90 return pcap.get<uint32_t>(); 91 } 92 93 bool PowerCap::getPcapEnabled() 94 { 95 auto settingService = getService(PCAP_PATH,PCAP_INTERFACE); 96 97 auto method = this->bus.new_method_call(settingService.c_str(), 98 PCAP_PATH, 99 "org.freedesktop.DBus.Properties", 100 "Get"); 101 102 method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP); 103 auto reply = this->bus.call(method); 104 105 if (reply.is_method_error()) 106 { 107 log<level::ERR>("Error in getPcapEnabled prop"); 108 return 0; 109 } 110 sdbusplus::message::variant<bool> pcapEnabled; 111 reply.read(pcapEnabled); 112 113 return pcapEnabled.get<bool>(); 114 } 115 116 void PowerCap::writeOcc(uint32_t pcapValue) 117 { 118 // Create path out to master occ hwmon entry 119 std::unique_ptr<fs::path> fileName = 120 std::make_unique<fs::path>(OCC_HWMON_PATH); 121 *fileName /= occMasterName; 122 *fileName /= "/hwmon/"; 123 124 // Need to get the hwmonXX directory name, there better only be 1 dir 125 assert(std::distance(fs::directory_iterator(*fileName), 126 fs::directory_iterator{}) == 1); 127 // Now set our path to this full path, including this hwmonXX directory 128 fileName = std::make_unique<fs::path>(*fs::directory_iterator(*fileName)); 129 // Append on the hwmon string where we write the user power cap 130 *fileName /= "/caps1_user"; 131 132 auto pcapString {std::to_string(pcapValue)}; 133 134 log<level::INFO>("Writing pcap value to hwmon", 135 entry("PCAP_PATH=%s",fileName->c_str()), 136 entry("PCAP_VALUE=%s",pcapString.c_str())); 137 // Open the hwmon file and write the power cap 138 std::ofstream file(*fileName, std::ios::out); 139 file << pcapString; 140 file.close(); 141 return; 142 } 143 144 void PowerCap::pcapChanged(sdbusplus::message::message& msg) 145 { 146 if (!occStatus.occActive()) 147 { 148 // Nothing to do 149 return; 150 } 151 152 uint32_t pcap = 0; 153 bool pcapEnabled = false; 154 155 std::string msgSensor; 156 std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData; 157 msg.read(msgSensor, msgData); 158 159 // Retrieve which property changed via the msg and read the other one 160 auto valPropMap = msgData.find(POWER_CAP_PROP); 161 if (valPropMap != msgData.end()) 162 { 163 pcap = sdbusplus::message::variant_ns::get<uint32_t>( 164 valPropMap->second); 165 pcapEnabled = getPcapEnabled(); 166 } 167 else 168 { 169 valPropMap = msgData.find(POWER_CAP_ENABLE_PROP); 170 if (valPropMap != msgData.end()) 171 { 172 pcapEnabled = sdbusplus::message::variant_ns::get<bool>( 173 valPropMap->second); 174 pcap = getPcap(); 175 } 176 else 177 { 178 log<level::INFO>("Unknown power cap property changed"); 179 return; 180 } 181 } 182 183 log<level::INFO>("Power Cap Property Change", 184 entry("PCAP=%u",pcap), 185 entry("PCAP_ENABLED=%u",pcapEnabled)); 186 187 // Determine desired action to write to occ 188 189 auto occInput = getOccInput(pcap, pcapEnabled); 190 191 // Write action to occ 192 writeOcc(occInput); 193 194 return; 195 } 196 197 } // namespace open_power 198 199 } // namespace occ 200 201 }// namespace powercap 202