1 /**
2  * Copyright © 2022 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "extensions/phal/clock_logger.hpp"
18 
19 #include "util.hpp"
20 
21 #include <attributes_info.H>
22 #include <libphal.H>
23 
24 #include <phosphor-logging/lg2.hpp>
25 #include <sdeventplus/event.hpp>
26 #include <sdeventplus/utility/timer.hpp>
27 
28 #include <chrono>
29 
30 using namespace openpower::pel;
31 
32 PHOSPHOR_LOG2_USING;
33 
34 namespace openpower::phal::clock
35 {
36 constexpr auto CLOCK_DAILY_LOGGER_TIMEOUT_IN_HOUR = 24;
37 
38 Manager::Manager(const sdeventplus::Event& event) :
39     _event(event), timer(event, std::bind(&Manager::timerExpired, this))
40 
41 {
42     try
43     {
44         // pdbg initialisation
45         openpower::phal::pdbg::init();
46 
47         // Create clock data log.
48         createClockDataLog();
49     }
50     catch (const std::exception& e)
51     {
52         error("Clock Data Log exception ({ERROR})", "ERROR", e);
53     }
54 
55     addTimer();
56 }
57 
58 void Manager::addTimer()
59 {
60     // Set timer for 24 hours.
61     timer.restart(std::chrono::hours(CLOCK_DAILY_LOGGER_TIMEOUT_IN_HOUR));
62 }
63 
64 void Manager::timerExpired()
65 {
66     info("Clock daily logging started");
67 
68     try
69     {
70         // Create clock data log.
71         createClockDataLog();
72     }
73     catch (const std::exception& e)
74     {
75         error("createClockDataLog exception ({ERROR})", "ERROR", e);
76     }
77 }
78 
79 void Manager::createClockDataLog()
80 {
81     // check chassis power state.
82     auto powerState = openpower::util::getChassisPowerState();
83 
84     if (powerState != "xyz.openbmc_project.State.Chassis.PowerState.On")
85     {
86         warning("The chassis power state({POWERSTATE}) is not ON, Skipping "
87                 "clock data "
88                 "logging",
89                 "POWERSTATE", powerState);
90         return;
91     }
92 
93     // Data logger storage
94     FFDCData clockDataLog;
95 
96     struct pdbg_target* procTarget;
97     ATTR_HWAS_STATE_Type hwasState;
98     pdbg_for_each_class_target("proc", procTarget)
99     {
100         if (DT_GET_PROP(ATTR_HWAS_STATE, procTarget, hwasState))
101         {
102             error("{TARGET} Could not read HWAS_STATE attribute", "TARGET",
103                   pdbg_target_path(procTarget));
104             continue;
105         }
106         if (!hwasState.present)
107         {
108             continue;
109         }
110 
111         auto index = std::to_string(pdbg_target_index(procTarget));
112 
113         // update functional State
114         std::string funState = "Non Functional";
115 
116         if (hwasState.functional)
117         {
118             funState = "Functional";
119         }
120         std::stringstream ssState;
121         ssState << "Proc" << index;
122         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
123 
124         // update location code information
125         ATTR_LOCATION_CODE_Type locationCode;
126         memset(&locationCode, '\0', sizeof(locationCode));
127         try
128         {
129             openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
130         }
131         catch (const std::exception& e)
132         {
133             error("getLocationCode on {TARGET} thrown exception ({ERROR})",
134                   "TARGET", pdbg_target_path(procTarget), "ERROR", e);
135         }
136         std::stringstream ssLoc;
137         ssLoc << "Proc" << index << " Location Code";
138         clockDataLog.push_back(std::make_pair(ssLoc.str(), locationCode));
139 
140         // Update Processor EC level
141         ATTR_EC_Type ecVal = 0;
142         if (DT_GET_PROP(ATTR_EC, procTarget, ecVal))
143         {
144             error("Could not read ATTR_EC  attribute");
145         }
146         std::stringstream ssEC;
147         ssEC << "Proc" << index << " EC";
148 
149         std::stringstream ssECVal;
150         ssECVal << "0x" << std::setfill('0') << std::setw(10) << std::hex
151                 << (uint16_t)ecVal;
152         clockDataLog.push_back(std::make_pair(ssEC.str(), ssECVal.str()));
153 
154         // Add CFAM register information.
155         addCFAMData(procTarget, clockDataLog);
156     }
157 
158     // Add clock register information
159     addClockRegData(clockDataLog);
160 
161     openpower::pel::createPEL("org.open_power.PHAL.Info.ClockDailyLog",
162                               clockDataLog);
163 }
164 
165 void Manager::addCFAMData(struct pdbg_target* proc,
166                           openpower::pel::FFDCData& clockDataLog)
167 {
168 
169     // collect Processor CFAM register data
170     const std::vector<int> procCFAMAddr = {
171         0x1007, 0x2804, 0x2810, 0x2813, 0x2814, 0x2815, 0x2816, 0x281D, 0x281E};
172 
173     auto index = std::to_string(pdbg_target_index(proc));
174 
175     for (int addr : procCFAMAddr)
176     {
177         auto val = 0xDEADBEEF;
178         try
179         {
180             val = openpower::phal::pdbg::getCFAM(proc, addr);
181         }
182         catch (const std::exception& e)
183         {
184             error("getCFAM on {TARGET} thrown exception({ERROR}): Addr ({REG})",
185                   "TARGET", pdbg_target_path(proc), "ERROR", e, "REG", addr);
186         }
187         std::stringstream ssData;
188         ssData << "0x" << std::setfill('0') << std::setw(8) << std::hex << val;
189         std::stringstream ssAddr;
190         ssAddr << "Proc" << index << " REG 0x" << std::hex << addr;
191         // update the data
192         clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
193     }
194 }
195 
196 void Manager::addClockRegData(openpower::pel::FFDCData& clockDataLog)
197 {
198     info("Adding clock register information to daily logger");
199 
200     struct pdbg_target* clockTarget;
201     pdbg_for_each_class_target("oscrefclk", clockTarget)
202     {
203         ATTR_HWAS_STATE_Type hwasState;
204         if (DT_GET_PROP(ATTR_HWAS_STATE, clockTarget, hwasState))
205         {
206             error("({TARGET}) Could not read HWAS_STATE attribute", "TARGET",
207                   pdbg_target_path(clockTarget));
208             continue;
209         }
210 
211         if (!hwasState.present)
212         {
213             continue;
214         }
215 
216         std::string funState = "Non Functional";
217 
218         if (hwasState.functional)
219         {
220             funState = "Functional";
221         }
222 
223         auto index = std::to_string(pdbg_target_index(clockTarget));
224 
225         std::stringstream ssState;
226         ssState << "Clock" << index;
227         clockDataLog.push_back(std::make_pair(ssState.str(), funState));
228 
229         // Add clcok device path information
230         std::stringstream ssName;
231         ssName << "Clock" << index << " path";
232         clockDataLog.push_back(
233             std::make_pair(ssName.str(), pdbg_target_path(clockTarget)));
234 
235         auto status = pdbg_target_probe(clockTarget);
236         if (status != PDBG_TARGET_ENABLED)
237         {
238             continue;
239         }
240 
241         // Update Buffer with clock I2C register data.
242         auto constexpr I2C_READ_SIZE = 0x08;
243         auto constexpr I2C_ADDR_MAX = 0xFF;
244 
245         for (auto addr = 0; addr <= I2C_ADDR_MAX; addr += I2C_READ_SIZE)
246         {
247             std::stringstream ssData;
248 
249             uint8_t data[0x8];
250             auto i2cRc = i2c_read(clockTarget, 0, addr, I2C_READ_SIZE, data);
251             if (i2cRc)
252             {
253                 error("({TARGET}) I2C read error({ERROR}) reported {ADDRESS} ",
254                       "TARGET", pdbg_target_path(clockTarget), "ERROR", i2cRc,
255                       "ADDRESS", addr);
256                 continue;
257             }
258 
259             for (auto i = 0; i < I2C_READ_SIZE; i++)
260             {
261                 ssData << " " << std::hex << std::setfill('0') << std::setw(2)
262                        << (uint16_t)data[i];
263             }
264             std::stringstream ssAddr;
265             ssAddr << "Clock" << index << "_0x" << std::hex << std::setfill('0')
266                    << std::setw(2) << addr;
267             clockDataLog.push_back(make_pair(ssAddr.str(), ssData.str()));
268         }
269     }
270 }
271 
272 } // namespace openpower::phal::clock
273