1 #include "config.h"
2 
3 #include "occ_manager.hpp"
4 
5 #include "i2c_occ.hpp"
6 #include "occ_finder.hpp"
7 #include "utils.hpp"
8 
9 #include <experimental/filesystem>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <phosphor-logging/log.hpp>
12 #include <xyz/openbmc_project/Common/error.hpp>
13 
14 namespace open_power
15 {
16 namespace occ
17 {
18 
19 using namespace phosphor::logging;
20 
21 void Manager::findAndCreateObjects()
22 {
23     for (auto id = 0; id < MAX_CPUS; ++id)
24     {
25         // Create one occ per cpu
26         auto occ = std::string(OCC_NAME) + std::to_string(id);
27         createObjects(occ);
28     }
29 }
30 
31 int Manager::cpuCreated(sdbusplus::message::message& msg)
32 {
33     namespace fs = std::experimental::filesystem;
34 
35     sdbusplus::message::object_path o;
36     msg.read(o);
37     fs::path cpuPath(std::string(std::move(o)));
38 
39     auto name = cpuPath.filename().string();
40     auto index = name.find(CPU_NAME);
41     name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
42 
43     createObjects(name);
44 
45     return 0;
46 }
47 
48 void Manager::createObjects(const std::string& occ)
49 {
50     auto path = fs::path(OCC_CONTROL_ROOT) / occ;
51 
52     passThroughObjects.emplace_back(
53         std::make_unique<PassThrough>(bus, path.c_str()));
54 
55     statusObjects.emplace_back(std::make_unique<Status>(
56         bus, event, path.c_str(), *this,
57         std::bind(std::mem_fn(&Manager::statusCallBack), this,
58                   std::placeholders::_1)
59 #ifdef PLDM
60             ,
61         std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
62                   std::placeholders::_1)
63 #endif
64             ));
65 
66     // Create the power cap monitor object for master occ (0)
67     if (!pcap)
68     {
69         pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
70             bus, *statusObjects.front());
71     }
72 }
73 
74 void Manager::statusCallBack(bool status)
75 {
76     using InternalFailure =
77         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
78 
79     // At this time, it won't happen but keeping it
80     // here just in case something changes in the future
81     if ((activeCount == 0) && (!status))
82     {
83         log<level::ERR>("Invalid update on OCCActive");
84         elog<InternalFailure>();
85     }
86 
87     activeCount += status ? 1 : -1;
88 
89     // Only start presence detection if all the OCCs are bound
90     if (activeCount == statusObjects.size())
91     {
92         for (auto& obj : statusObjects)
93         {
94             obj->addPresenceWatchMaster();
95         }
96     }
97 
98     if ((!_pollTimer->isEnabled()) && (activeCount > 0))
99     {
100         log<level::INFO>(fmt::format("Manager::statusCallBack(): {} OCCs will "
101                                      "be polled every {} seconds",
102                                      activeCount, pollInterval)
103                              .c_str());
104 
105         // Send poll and start OCC poll timer
106         pollerTimerExpired();
107     }
108     else if ((_pollTimer->isEnabled()) && (activeCount == 0))
109     {
110         // Stop OCC poll timer
111         log<level::INFO>("Manager::statusCallBack(): OCCs are not running, "
112                          "stopping poll timer");
113         _pollTimer->setEnabled(false);
114     }
115 }
116 
117 #ifdef I2C_OCC
118 void Manager::initStatusObjects()
119 {
120     // Make sure we have a valid path string
121     static_assert(sizeof(DEV_PATH) != 0);
122 
123     auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
124     auto occMasterName = deviceNames.front();
125     for (auto& name : deviceNames)
126     {
127         i2c_occ::i2cToDbus(name);
128         name = std::string(OCC_NAME) + '_' + name;
129         auto path = fs::path(OCC_CONTROL_ROOT) / name;
130         statusObjects.emplace_back(
131             std::make_unique<Status>(bus, event, path.c_str(), *this));
132     }
133     // The first device is master occ
134     pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
135         bus, *statusObjects.front(), occMasterName);
136 }
137 #endif
138 
139 #ifdef PLDM
140 bool Manager::updateOCCActive(instanceID instance, bool status)
141 {
142     return (statusObjects[instance])->occActive(status);
143 }
144 #endif
145 
146 void Manager::pollerTimerExpired()
147 {
148     if (activeCount == 0)
149     {
150         // No OCCs running, so poll timer will not be restarted
151         log<level::INFO>("Manager::pollerTimerExpire(): No OCCs running, poll "
152                          "timer not restarted");
153     }
154 
155     if (!_pollTimer)
156     {
157         log<level::ERR>(
158             "Manager::pollerTimerExpired() ERROR: Timer not defined");
159         return;
160     }
161 
162     for (auto& obj : statusObjects)
163     {
164         // Read sysfs to force kernel to poll OCC
165         obj->readOccState();
166     }
167 
168     // Restart OCC poll timer
169     _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
170 }
171 
172 } // namespace occ
173 } // namespace open_power
174