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::pcapChanged(sdbusplus::message::message& msg)
117 {
118     if (!occStatus.occActive())
119     {
120         // Nothing to  do
121         return;
122     }
123 
124     uint32_t pcap = 0;
125     bool pcapEnabled = false;
126 
127     std::string msgSensor;
128     std::map<std::string, sdbusplus::message::variant<uint32_t, bool>> msgData;
129     msg.read(msgSensor, msgData);
130 
131     // Retrieve which property changed via the msg and read the other one
132     auto valPropMap = msgData.find(POWER_CAP_PROP);
133     if (valPropMap != msgData.end())
134     {
135         pcap = sdbusplus::message::variant_ns::get<uint32_t>(
136             valPropMap->second);
137         pcapEnabled = getPcapEnabled();
138     }
139     else
140     {
141         valPropMap = msgData.find(POWER_CAP_ENABLE_PROP);
142         if (valPropMap != msgData.end())
143         {
144             pcapEnabled = sdbusplus::message::variant_ns::get<bool>(
145                 valPropMap->second);
146             pcap = getPcap();
147         }
148         else
149         {
150             log<level::INFO>("Unknown power cap property changed");
151             return;
152         }
153     }
154 
155     log<level::INFO>("Power Cap Property Change",
156                      entry("PCAP=%u",pcap),
157                      entry("PCAP_ENABLED=%u",pcapEnabled));
158 
159     // Determine desired action to write to occ
160     auto occInput = getOccInput(pcap, pcapEnabled);
161     log<level::DEBUG>("Writing new power cap setting to OCC",
162                      entry("OCC_PCAP_VAL=%u",occInput));
163 
164     // Write action to occ
165     // TODO
166 
167     return;
168 }
169 
170 } // namespace open_power
171 
172 } // namespace occ
173 
174 }// namespace powercap
175