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