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