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