xref: /openbmc/openpower-occ-control/powercap.cpp (revision 94df8c9015798764d40665bb363bdbf27510f285)
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