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 /= OCC_MASTER_NAME;
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),
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