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