xref: /openbmc/smbios-mdr/src/sst_mailbox.cpp (revision 16a2ced3745d358a83d1094f38a3d87c51c58404)
1*16a2ced3SJonathan Doman // Copyright (c) 2022 Intel Corporation
2*16a2ced3SJonathan Doman //
3*16a2ced3SJonathan Doman // Licensed under the Apache License, Version 2.0 (the "License");
4*16a2ced3SJonathan Doman // you may not use this file except in compliance with the License.
5*16a2ced3SJonathan Doman // You may obtain a copy of the License at
6*16a2ced3SJonathan Doman //
7*16a2ced3SJonathan Doman //      http://www.apache.org/licenses/LICENSE-2.0
8*16a2ced3SJonathan Doman //
9*16a2ced3SJonathan Doman // Unless required by applicable law or agreed to in writing, software
10*16a2ced3SJonathan Doman // distributed under the License is distributed on an "AS IS" BASIS,
11*16a2ced3SJonathan Doman // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*16a2ced3SJonathan Doman // See the License for the specific language governing permissions and
13*16a2ced3SJonathan Doman // limitations under the License.
14*16a2ced3SJonathan Doman 
15*16a2ced3SJonathan Doman #include "cpuinfo_utils.hpp"
16*16a2ced3SJonathan Doman #include "speed_select.hpp"
17*16a2ced3SJonathan Doman 
18*16a2ced3SJonathan Doman #include <iostream>
19*16a2ced3SJonathan Doman 
20*16a2ced3SJonathan Doman namespace cpu_info
21*16a2ced3SJonathan Doman {
22*16a2ced3SJonathan Doman namespace sst
23*16a2ced3SJonathan Doman {
24*16a2ced3SJonathan Doman 
25*16a2ced3SJonathan Doman /**
26*16a2ced3SJonathan Doman  * Convenience RAII object for Wake-On-PECI (WOP) management, since PECI Config
27*16a2ced3SJonathan Doman  * Local accesses to the OS Mailbox require the package to pop up to PC2. Also
28*16a2ced3SJonathan Doman  * provides PCode OS Mailbox routine.
29*16a2ced3SJonathan Doman  *
30*16a2ced3SJonathan Doman  * Since multiple applications may be modifing WOP, we'll use this algorithm:
31*16a2ced3SJonathan Doman  * Whenever a PECI command fails with associated error code, set WOP bit and
32*16a2ced3SJonathan Doman  * retry command. Upon manager destruction, clear WOP bit only if we previously
33*16a2ced3SJonathan Doman  * set it.
34*16a2ced3SJonathan Doman  */
35*16a2ced3SJonathan Doman struct PECIManager
36*16a2ced3SJonathan Doman {
37*16a2ced3SJonathan Doman     uint8_t peciAddress;
38*16a2ced3SJonathan Doman     bool peciWoken;
39*16a2ced3SJonathan Doman     CPUModel cpuModel;
40*16a2ced3SJonathan Doman     uint8_t mbBus;
41*16a2ced3SJonathan Doman 
42*16a2ced3SJonathan Doman     PECIManager(uint8_t address, CPUModel model) :
43*16a2ced3SJonathan Doman         peciAddress(address), peciWoken(false), cpuModel(model)
44*16a2ced3SJonathan Doman     {
45*16a2ced3SJonathan Doman         mbBus = (model == icx) ? mbBusICX : mbBusOther;
46*16a2ced3SJonathan Doman     }
47*16a2ced3SJonathan Doman 
48*16a2ced3SJonathan Doman     ~PECIManager()
49*16a2ced3SJonathan Doman     {
50*16a2ced3SJonathan Doman         // If we're being destroyed due to a PECIError, try to clear the mode
51*16a2ced3SJonathan Doman         // bit, but catch and ignore any duplicate error it might raise to
52*16a2ced3SJonathan Doman         // prevent termination.
53*16a2ced3SJonathan Doman         try
54*16a2ced3SJonathan Doman         {
55*16a2ced3SJonathan Doman             if (peciWoken)
56*16a2ced3SJonathan Doman             {
57*16a2ced3SJonathan Doman                 setWakeOnPECI(false);
58*16a2ced3SJonathan Doman             }
59*16a2ced3SJonathan Doman         }
60*16a2ced3SJonathan Doman         catch (const PECIError& err)
61*16a2ced3SJonathan Doman         {}
62*16a2ced3SJonathan Doman     }
63*16a2ced3SJonathan Doman 
64*16a2ced3SJonathan Doman     static bool isSleeping(EPECIStatus libStatus, uint8_t completionCode)
65*16a2ced3SJonathan Doman     {
66*16a2ced3SJonathan Doman         // PECI completion code defined in peci-ioctl.h which is not available
67*16a2ced3SJonathan Doman         // for us to include.
68*16a2ced3SJonathan Doman         constexpr int PECI_DEV_CC_UNAVAIL_RESOURCE = 0x82;
69*16a2ced3SJonathan Doman         // Observed library returning DRIVER_ERR for reads and TIMEOUT for
70*16a2ced3SJonathan Doman         // writes while PECI is sleeping. Either way, the completion code from
71*16a2ced3SJonathan Doman         // PECI client should be reliable indicator of need to set WOP.
72*16a2ced3SJonathan Doman         return libStatus != PECI_CC_SUCCESS &&
73*16a2ced3SJonathan Doman                completionCode == PECI_DEV_CC_UNAVAIL_RESOURCE;
74*16a2ced3SJonathan Doman     }
75*16a2ced3SJonathan Doman 
76*16a2ced3SJonathan Doman     /**
77*16a2ced3SJonathan Doman      * Send a single PECI PCS write to modify the Wake-On-PECI mode bit
78*16a2ced3SJonathan Doman      */
79*16a2ced3SJonathan Doman     void setWakeOnPECI(bool enable)
80*16a2ced3SJonathan Doman     {
81*16a2ced3SJonathan Doman         uint8_t completionCode;
82*16a2ced3SJonathan Doman         EPECIStatus libStatus =
83*16a2ced3SJonathan Doman             peci_WrPkgConfig(peciAddress, 5, enable ? 1 : 0, 0,
84*16a2ced3SJonathan Doman                              sizeof(uint32_t), &completionCode);
85*16a2ced3SJonathan Doman         if (!checkPECIStatus(libStatus, completionCode))
86*16a2ced3SJonathan Doman         {
87*16a2ced3SJonathan Doman             throw PECIError("Failed to set Wake-On-PECI mode bit");
88*16a2ced3SJonathan Doman         }
89*16a2ced3SJonathan Doman 
90*16a2ced3SJonathan Doman         if (enable)
91*16a2ced3SJonathan Doman         {
92*16a2ced3SJonathan Doman             peciWoken = true;
93*16a2ced3SJonathan Doman         }
94*16a2ced3SJonathan Doman     }
95*16a2ced3SJonathan Doman 
96*16a2ced3SJonathan Doman     // PCode OS Mailbox interface register locations
97*16a2ced3SJonathan Doman     static constexpr int mbBusICX = 14;
98*16a2ced3SJonathan Doman     static constexpr int mbBusOther = 31;
99*16a2ced3SJonathan Doman     static constexpr int mbSegment = 0;
100*16a2ced3SJonathan Doman     static constexpr int mbDevice = 30;
101*16a2ced3SJonathan Doman     static constexpr int mbFunction = 1;
102*16a2ced3SJonathan Doman     static constexpr int mbDataReg = 0xA0;
103*16a2ced3SJonathan Doman     static constexpr int mbInterfaceReg = 0xA4;
104*16a2ced3SJonathan Doman     static constexpr int mbRegSize = sizeof(uint32_t);
105*16a2ced3SJonathan Doman 
106*16a2ced3SJonathan Doman     enum class MailboxStatus
107*16a2ced3SJonathan Doman     {
108*16a2ced3SJonathan Doman         NoError = 0x0,
109*16a2ced3SJonathan Doman         InvalidCommand = 0x1,
110*16a2ced3SJonathan Doman         IllegalData = 0x16
111*16a2ced3SJonathan Doman     };
112*16a2ced3SJonathan Doman 
113*16a2ced3SJonathan Doman     /**
114*16a2ced3SJonathan Doman      * Send a single Write PCI Config Local command, targeting the PCU CR1
115*16a2ced3SJonathan Doman      * register block.
116*16a2ced3SJonathan Doman      *
117*16a2ced3SJonathan Doman      * @param[in]   regAddress  PCI Offset of register.
118*16a2ced3SJonathan Doman      * @param[in]   data        Data to write.
119*16a2ced3SJonathan Doman      */
120*16a2ced3SJonathan Doman     void wrMailboxReg(uint16_t regAddress, uint32_t data)
121*16a2ced3SJonathan Doman     {
122*16a2ced3SJonathan Doman         uint8_t completionCode;
123*16a2ced3SJonathan Doman         bool tryWaking = true;
124*16a2ced3SJonathan Doman         while (true)
125*16a2ced3SJonathan Doman         {
126*16a2ced3SJonathan Doman             EPECIStatus libStatus = peci_WrEndPointPCIConfigLocal(
127*16a2ced3SJonathan Doman                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
128*16a2ced3SJonathan Doman                 mbRegSize, data, &completionCode);
129*16a2ced3SJonathan Doman             if (tryWaking && isSleeping(libStatus, completionCode))
130*16a2ced3SJonathan Doman             {
131*16a2ced3SJonathan Doman                 setWakeOnPECI(true);
132*16a2ced3SJonathan Doman                 tryWaking = false;
133*16a2ced3SJonathan Doman                 continue;
134*16a2ced3SJonathan Doman             }
135*16a2ced3SJonathan Doman             else if (!checkPECIStatus(libStatus, completionCode))
136*16a2ced3SJonathan Doman             {
137*16a2ced3SJonathan Doman                 throw PECIError("Failed to write mailbox reg");
138*16a2ced3SJonathan Doman             }
139*16a2ced3SJonathan Doman             break;
140*16a2ced3SJonathan Doman         }
141*16a2ced3SJonathan Doman     }
142*16a2ced3SJonathan Doman 
143*16a2ced3SJonathan Doman     /**
144*16a2ced3SJonathan Doman      * Send a single Read PCI Config Local command, targeting the PCU CR1
145*16a2ced3SJonathan Doman      * register block.
146*16a2ced3SJonathan Doman      *
147*16a2ced3SJonathan Doman      * @param[in]   regAddress  PCI offset of register.
148*16a2ced3SJonathan Doman      *
149*16a2ced3SJonathan Doman      * @return  Register value
150*16a2ced3SJonathan Doman      */
151*16a2ced3SJonathan Doman     uint32_t rdMailboxReg(uint16_t regAddress)
152*16a2ced3SJonathan Doman     {
153*16a2ced3SJonathan Doman         uint8_t completionCode;
154*16a2ced3SJonathan Doman         uint32_t outputData;
155*16a2ced3SJonathan Doman         bool tryWaking = true;
156*16a2ced3SJonathan Doman         while (true)
157*16a2ced3SJonathan Doman         {
158*16a2ced3SJonathan Doman             EPECIStatus libStatus = peci_RdEndPointConfigPciLocal(
159*16a2ced3SJonathan Doman                 peciAddress, mbSegment, mbBus, mbDevice, mbFunction, regAddress,
160*16a2ced3SJonathan Doman                 mbRegSize, reinterpret_cast<uint8_t*>(&outputData),
161*16a2ced3SJonathan Doman                 &completionCode);
162*16a2ced3SJonathan Doman             if (tryWaking && isSleeping(libStatus, completionCode))
163*16a2ced3SJonathan Doman             {
164*16a2ced3SJonathan Doman                 setWakeOnPECI(true);
165*16a2ced3SJonathan Doman                 tryWaking = false;
166*16a2ced3SJonathan Doman                 continue;
167*16a2ced3SJonathan Doman             }
168*16a2ced3SJonathan Doman             if (!checkPECIStatus(libStatus, completionCode))
169*16a2ced3SJonathan Doman             {
170*16a2ced3SJonathan Doman                 throw PECIError("Failed to read mailbox reg");
171*16a2ced3SJonathan Doman             }
172*16a2ced3SJonathan Doman             break;
173*16a2ced3SJonathan Doman         }
174*16a2ced3SJonathan Doman         return outputData;
175*16a2ced3SJonathan Doman     }
176*16a2ced3SJonathan Doman 
177*16a2ced3SJonathan Doman     /**
178*16a2ced3SJonathan Doman      * Send command on PCode OS Mailbox interface.
179*16a2ced3SJonathan Doman      *
180*16a2ced3SJonathan Doman      * @param[in]   command     Main command ID.
181*16a2ced3SJonathan Doman      * @param[in]   subCommand  Sub command ID.
182*16a2ced3SJonathan Doman      * @param[in]   inputData   Data to put in mailbox. Is always written, but
183*16a2ced3SJonathan Doman      *                          will be ignored by PCode if command is a
184*16a2ced3SJonathan Doman      *                          "getter".
185*16a2ced3SJonathan Doman      * @param[out]  responseCode    Optional parameter to receive the
186*16a2ced3SJonathan Doman      *                              mailbox-level response status. If null, a
187*16a2ced3SJonathan Doman      *                              PECIError will be thrown for error status.
188*16a2ced3SJonathan Doman      *
189*16a2ced3SJonathan Doman      * @return  Data returned in mailbox. Value is undefined if command is a
190*16a2ced3SJonathan Doman      *          "setter".
191*16a2ced3SJonathan Doman      */
192*16a2ced3SJonathan Doman     uint32_t sendPECIOSMailboxCmd(uint8_t command, uint8_t subCommand,
193*16a2ced3SJonathan Doman                                   uint32_t inputData = 0,
194*16a2ced3SJonathan Doman                                   MailboxStatus* responseCode = nullptr)
195*16a2ced3SJonathan Doman     {
196*16a2ced3SJonathan Doman         // The simple mailbox algorithm just says to wait until the busy bit
197*16a2ced3SJonathan Doman         // is clear, but we'll give up after 10 tries. It's arbitrary but that's
198*16a2ced3SJonathan Doman         // quite long wall clock time.
199*16a2ced3SJonathan Doman         constexpr int mbRetries = 10;
200*16a2ced3SJonathan Doman         constexpr uint32_t mbBusyBit = bit(31);
201*16a2ced3SJonathan Doman 
202*16a2ced3SJonathan Doman         // Wait until RUN_BUSY == 0
203*16a2ced3SJonathan Doman         int attempts = mbRetries;
204*16a2ced3SJonathan Doman         while ((rdMailboxReg(mbInterfaceReg) & mbBusyBit) != 0 &&
205*16a2ced3SJonathan Doman                --attempts > 0)
206*16a2ced3SJonathan Doman             ;
207*16a2ced3SJonathan Doman         if (attempts == 0)
208*16a2ced3SJonathan Doman         {
209*16a2ced3SJonathan Doman             throw PECIError("OS Mailbox failed to become free");
210*16a2ced3SJonathan Doman         }
211*16a2ced3SJonathan Doman 
212*16a2ced3SJonathan Doman         // Write required command specific input data to data register
213*16a2ced3SJonathan Doman         wrMailboxReg(mbDataReg, inputData);
214*16a2ced3SJonathan Doman 
215*16a2ced3SJonathan Doman         // Write required command specific command/sub-command values and set
216*16a2ced3SJonathan Doman         // RUN_BUSY bit in interface register.
217*16a2ced3SJonathan Doman         uint32_t interfaceReg =
218*16a2ced3SJonathan Doman             mbBusyBit | (static_cast<uint32_t>(subCommand) << 8) | command;
219*16a2ced3SJonathan Doman         wrMailboxReg(mbInterfaceReg, interfaceReg);
220*16a2ced3SJonathan Doman 
221*16a2ced3SJonathan Doman         // Wait until RUN_BUSY == 0
222*16a2ced3SJonathan Doman         attempts = mbRetries;
223*16a2ced3SJonathan Doman         do
224*16a2ced3SJonathan Doman         {
225*16a2ced3SJonathan Doman             interfaceReg = rdMailboxReg(mbInterfaceReg);
226*16a2ced3SJonathan Doman         } while ((interfaceReg & mbBusyBit) != 0 && --attempts > 0);
227*16a2ced3SJonathan Doman         if (attempts == 0)
228*16a2ced3SJonathan Doman         {
229*16a2ced3SJonathan Doman             throw PECIError("OS Mailbox failed to return");
230*16a2ced3SJonathan Doman         }
231*16a2ced3SJonathan Doman 
232*16a2ced3SJonathan Doman         // Read command return status or error code from interface register
233*16a2ced3SJonathan Doman         auto status = static_cast<MailboxStatus>(interfaceReg & 0xFF);
234*16a2ced3SJonathan Doman         if (responseCode != nullptr)
235*16a2ced3SJonathan Doman         {
236*16a2ced3SJonathan Doman             *responseCode = status;
237*16a2ced3SJonathan Doman         }
238*16a2ced3SJonathan Doman         else if (status != MailboxStatus::NoError)
239*16a2ced3SJonathan Doman         {
240*16a2ced3SJonathan Doman             throw PECIError(std::string("OS Mailbox returned with error: ") +
241*16a2ced3SJonathan Doman                             std::to_string(static_cast<int>(status)));
242*16a2ced3SJonathan Doman         }
243*16a2ced3SJonathan Doman 
244*16a2ced3SJonathan Doman         // Read command return data from the data register
245*16a2ced3SJonathan Doman         return rdMailboxReg(mbDataReg);
246*16a2ced3SJonathan Doman     }
247*16a2ced3SJonathan Doman };
248*16a2ced3SJonathan Doman 
249*16a2ced3SJonathan Doman /**
250*16a2ced3SJonathan Doman  * Base class for set of PECI OS Mailbox commands.
251*16a2ced3SJonathan Doman  * Constructing it runs the command and stores the value for use by derived
252*16a2ced3SJonathan Doman  * class accessor methods.
253*16a2ced3SJonathan Doman  */
254*16a2ced3SJonathan Doman template <uint8_t subcommand>
255*16a2ced3SJonathan Doman struct OsMailboxCommand
256*16a2ced3SJonathan Doman {
257*16a2ced3SJonathan Doman     enum ErrorPolicy
258*16a2ced3SJonathan Doman     {
259*16a2ced3SJonathan Doman         Throw,
260*16a2ced3SJonathan Doman         NoThrow
261*16a2ced3SJonathan Doman     };
262*16a2ced3SJonathan Doman 
263*16a2ced3SJonathan Doman     uint32_t value;
264*16a2ced3SJonathan Doman     PECIManager::MailboxStatus status;
265*16a2ced3SJonathan Doman     /**
266*16a2ced3SJonathan Doman      * Construct the command object with required PECI address and up to 4
267*16a2ced3SJonathan Doman      * optional 1-byte input data parameters.
268*16a2ced3SJonathan Doman      */
269*16a2ced3SJonathan Doman     OsMailboxCommand(PECIManager& pm, uint8_t param1 = 0, uint8_t param2 = 0,
270*16a2ced3SJonathan Doman                      uint8_t param3 = 0, uint8_t param4 = 0) :
271*16a2ced3SJonathan Doman         OsMailboxCommand(pm, ErrorPolicy::Throw, param1, param2, param3, param4)
272*16a2ced3SJonathan Doman     {}
273*16a2ced3SJonathan Doman 
274*16a2ced3SJonathan Doman     OsMailboxCommand(PECIManager& pm, ErrorPolicy errorPolicy,
275*16a2ced3SJonathan Doman                      uint8_t param1 = 0, uint8_t param2 = 0, uint8_t param3 = 0,
276*16a2ced3SJonathan Doman                      uint8_t param4 = 0)
277*16a2ced3SJonathan Doman     {
278*16a2ced3SJonathan Doman         DEBUG_PRINT << "Running OS Mailbox command "
279*16a2ced3SJonathan Doman                     << static_cast<int>(subcommand) << '\n';
280*16a2ced3SJonathan Doman         PECIManager::MailboxStatus* callStatus =
281*16a2ced3SJonathan Doman             errorPolicy == Throw ? nullptr : &status;
282*16a2ced3SJonathan Doman         uint32_t param = (static_cast<uint32_t>(param4) << 24) |
283*16a2ced3SJonathan Doman                          (static_cast<uint32_t>(param3) << 16) |
284*16a2ced3SJonathan Doman                          (static_cast<uint32_t>(param2) << 8) | param1;
285*16a2ced3SJonathan Doman         value = pm.sendPECIOSMailboxCmd(0x7F, subcommand, param, callStatus);
286*16a2ced3SJonathan Doman     }
287*16a2ced3SJonathan Doman 
288*16a2ced3SJonathan Doman     /** Return whether the mailbox status indicated success or not. */
289*16a2ced3SJonathan Doman     bool success() const
290*16a2ced3SJonathan Doman     {
291*16a2ced3SJonathan Doman         return status == PECIManager::MailboxStatus::NoError;
292*16a2ced3SJonathan Doman     }
293*16a2ced3SJonathan Doman };
294*16a2ced3SJonathan Doman 
295*16a2ced3SJonathan Doman /**
296*16a2ced3SJonathan Doman  * Macro to define a derived class accessor method.
297*16a2ced3SJonathan Doman  *
298*16a2ced3SJonathan Doman  * @param[in]   type    Return type of accessor method.
299*16a2ced3SJonathan Doman  * @param[in]   name    Name of accessor method.
300*16a2ced3SJonathan Doman  * @param[in]   hibit   Most significant bit of field to access.
301*16a2ced3SJonathan Doman  * @param[in]   lobit   Least significant bit of field to access.
302*16a2ced3SJonathan Doman  */
303*16a2ced3SJonathan Doman #define FIELD(type, name, hibit, lobit)                                        \
304*16a2ced3SJonathan Doman     type name() const                                                          \
305*16a2ced3SJonathan Doman     {                                                                          \
306*16a2ced3SJonathan Doman         return (value >> lobit) & (bit(hibit - lobit + 1) - 1);                \
307*16a2ced3SJonathan Doman     }
308*16a2ced3SJonathan Doman 
309*16a2ced3SJonathan Doman struct GetLevelsInfo : OsMailboxCommand<0x0>
310*16a2ced3SJonathan Doman {
311*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
312*16a2ced3SJonathan Doman     FIELD(bool, enabled, 31, 31)
313*16a2ced3SJonathan Doman     FIELD(bool, lock, 24, 24)
314*16a2ced3SJonathan Doman     FIELD(unsigned, currentConfigTdpLevel, 23, 16)
315*16a2ced3SJonathan Doman     FIELD(unsigned, configTdpLevels, 15, 8)
316*16a2ced3SJonathan Doman     FIELD(unsigned, version, 7, 0)
317*16a2ced3SJonathan Doman };
318*16a2ced3SJonathan Doman 
319*16a2ced3SJonathan Doman struct GetConfigTdpControl : OsMailboxCommand<0x1>
320*16a2ced3SJonathan Doman {
321*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
322*16a2ced3SJonathan Doman     FIELD(bool, pbfEnabled, 17, 17);
323*16a2ced3SJonathan Doman     FIELD(bool, factEnabled, 16, 16);
324*16a2ced3SJonathan Doman     FIELD(bool, pbfSupport, 1, 1);
325*16a2ced3SJonathan Doman     FIELD(bool, factSupport, 0, 0);
326*16a2ced3SJonathan Doman };
327*16a2ced3SJonathan Doman 
328*16a2ced3SJonathan Doman struct SetConfigTdpControl : OsMailboxCommand<0x2>
329*16a2ced3SJonathan Doman {
330*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
331*16a2ced3SJonathan Doman };
332*16a2ced3SJonathan Doman 
333*16a2ced3SJonathan Doman struct GetTdpInfo : OsMailboxCommand<0x3>
334*16a2ced3SJonathan Doman {
335*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
336*16a2ced3SJonathan Doman     FIELD(unsigned, tdpRatio, 23, 16);
337*16a2ced3SJonathan Doman     FIELD(unsigned, pkgTdp, 14, 0);
338*16a2ced3SJonathan Doman };
339*16a2ced3SJonathan Doman 
340*16a2ced3SJonathan Doman struct GetCoreMask : OsMailboxCommand<0x6>
341*16a2ced3SJonathan Doman {
342*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
343*16a2ced3SJonathan Doman     FIELD(uint32_t, coresMask, 31, 0);
344*16a2ced3SJonathan Doman };
345*16a2ced3SJonathan Doman 
346*16a2ced3SJonathan Doman struct GetTurboLimitRatios : OsMailboxCommand<0x7>
347*16a2ced3SJonathan Doman {
348*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
349*16a2ced3SJonathan Doman };
350*16a2ced3SJonathan Doman 
351*16a2ced3SJonathan Doman struct SetLevel : OsMailboxCommand<0x8>
352*16a2ced3SJonathan Doman {
353*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
354*16a2ced3SJonathan Doman };
355*16a2ced3SJonathan Doman 
356*16a2ced3SJonathan Doman struct GetRatioInfo : OsMailboxCommand<0xC>
357*16a2ced3SJonathan Doman {
358*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
359*16a2ced3SJonathan Doman     FIELD(unsigned, pm, 31, 24);
360*16a2ced3SJonathan Doman     FIELD(unsigned, pn, 23, 16);
361*16a2ced3SJonathan Doman     FIELD(unsigned, p1, 15, 8);
362*16a2ced3SJonathan Doman     FIELD(unsigned, p0, 7, 0);
363*16a2ced3SJonathan Doman };
364*16a2ced3SJonathan Doman 
365*16a2ced3SJonathan Doman struct GetTjmaxInfo : OsMailboxCommand<0x5>
366*16a2ced3SJonathan Doman {
367*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
368*16a2ced3SJonathan Doman     FIELD(unsigned, tProchot, 7, 0);
369*16a2ced3SJonathan Doman };
370*16a2ced3SJonathan Doman 
371*16a2ced3SJonathan Doman struct PbfGetCoreMaskInfo : OsMailboxCommand<0x20>
372*16a2ced3SJonathan Doman {
373*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
374*16a2ced3SJonathan Doman     FIELD(uint32_t, p1HiCoreMask, 31, 0);
375*16a2ced3SJonathan Doman };
376*16a2ced3SJonathan Doman 
377*16a2ced3SJonathan Doman struct PbfGetP1HiP1LoInfo : OsMailboxCommand<0x21>
378*16a2ced3SJonathan Doman {
379*16a2ced3SJonathan Doman     using OsMailboxCommand::OsMailboxCommand;
380*16a2ced3SJonathan Doman     FIELD(unsigned, p1Hi, 15, 8);
381*16a2ced3SJonathan Doman     FIELD(unsigned, p1Lo, 7, 0);
382*16a2ced3SJonathan Doman };
383*16a2ced3SJonathan Doman 
384*16a2ced3SJonathan Doman /**
385*16a2ced3SJonathan Doman  * Implementation of SSTInterface based on OS Mailbox interface supported on ICX
386*16a2ced3SJonathan Doman  * and SPR processors.
387*16a2ced3SJonathan Doman  * It's expected that an instance of this class will be created for each
388*16a2ced3SJonathan Doman  * "atomic" set of operations.
389*16a2ced3SJonathan Doman  */
390*16a2ced3SJonathan Doman class SSTMailbox : public SSTInterface
391*16a2ced3SJonathan Doman {
392*16a2ced3SJonathan Doman   private:
393*16a2ced3SJonathan Doman     uint8_t address;
394*16a2ced3SJonathan Doman     CPUModel model;
395*16a2ced3SJonathan Doman     PECIManager pm;
396*16a2ced3SJonathan Doman 
397*16a2ced3SJonathan Doman     static constexpr int mhzPerRatio = 100;
398*16a2ced3SJonathan Doman 
399*16a2ced3SJonathan Doman   public:
400*16a2ced3SJonathan Doman     SSTMailbox(uint8_t _address, CPUModel _model) :
401*16a2ced3SJonathan Doman         address(_address), model(_model),
402*16a2ced3SJonathan Doman         pm(static_cast<uint8_t>(address), model)
403*16a2ced3SJonathan Doman     {}
404*16a2ced3SJonathan Doman     ~SSTMailbox()
405*16a2ced3SJonathan Doman     {}
406*16a2ced3SJonathan Doman 
407*16a2ced3SJonathan Doman     bool ready() override
408*16a2ced3SJonathan Doman     {
409*16a2ced3SJonathan Doman         return true;
410*16a2ced3SJonathan Doman     }
411*16a2ced3SJonathan Doman 
412*16a2ced3SJonathan Doman     bool supportsControl() override
413*16a2ced3SJonathan Doman     {
414*16a2ced3SJonathan Doman         return model == spr;
415*16a2ced3SJonathan Doman     }
416*16a2ced3SJonathan Doman 
417*16a2ced3SJonathan Doman     unsigned int currentLevel() override
418*16a2ced3SJonathan Doman     {
419*16a2ced3SJonathan Doman         return GetLevelsInfo(pm).currentConfigTdpLevel();
420*16a2ced3SJonathan Doman     }
421*16a2ced3SJonathan Doman     unsigned int numLevels() override
422*16a2ced3SJonathan Doman     {
423*16a2ced3SJonathan Doman         return GetLevelsInfo(pm).configTdpLevels();
424*16a2ced3SJonathan Doman     }
425*16a2ced3SJonathan Doman     bool ppEnabled() override
426*16a2ced3SJonathan Doman     {
427*16a2ced3SJonathan Doman         return GetLevelsInfo(pm).enabled();
428*16a2ced3SJonathan Doman     }
429*16a2ced3SJonathan Doman 
430*16a2ced3SJonathan Doman     bool levelSupported(unsigned int level) override
431*16a2ced3SJonathan Doman     {
432*16a2ced3SJonathan Doman         GetConfigTdpControl tdpControl(
433*16a2ced3SJonathan Doman             pm, GetConfigTdpControl::ErrorPolicy::NoThrow,
434*16a2ced3SJonathan Doman             static_cast<uint8_t>(level));
435*16a2ced3SJonathan Doman         return tdpControl.success();
436*16a2ced3SJonathan Doman     }
437*16a2ced3SJonathan Doman     bool bfSupported(unsigned int level) override
438*16a2ced3SJonathan Doman     {
439*16a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
440*16a2ced3SJonathan Doman             .pbfSupport();
441*16a2ced3SJonathan Doman     }
442*16a2ced3SJonathan Doman     bool tfSupported(unsigned int level) override
443*16a2ced3SJonathan Doman     {
444*16a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
445*16a2ced3SJonathan Doman             .factSupport();
446*16a2ced3SJonathan Doman     }
447*16a2ced3SJonathan Doman     bool bfEnabled(unsigned int level) override
448*16a2ced3SJonathan Doman     {
449*16a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
450*16a2ced3SJonathan Doman             .pbfEnabled();
451*16a2ced3SJonathan Doman     }
452*16a2ced3SJonathan Doman     bool tfEnabled(unsigned int level) override
453*16a2ced3SJonathan Doman     {
454*16a2ced3SJonathan Doman         return GetConfigTdpControl(pm, static_cast<uint8_t>(level))
455*16a2ced3SJonathan Doman             .factEnabled();
456*16a2ced3SJonathan Doman     }
457*16a2ced3SJonathan Doman     unsigned int tdp(unsigned int level) override
458*16a2ced3SJonathan Doman     {
459*16a2ced3SJonathan Doman         return GetTdpInfo(pm, static_cast<uint8_t>(level)).pkgTdp();
460*16a2ced3SJonathan Doman     }
461*16a2ced3SJonathan Doman     unsigned int coreCount(unsigned int level) override
462*16a2ced3SJonathan Doman     {
463*16a2ced3SJonathan Doman         return enabledCoreList(level).size();
464*16a2ced3SJonathan Doman     }
465*16a2ced3SJonathan Doman     std::vector<unsigned int> enabledCoreList(unsigned int level) override
466*16a2ced3SJonathan Doman     {
467*16a2ced3SJonathan Doman         uint64_t coreMaskLo =
468*16a2ced3SJonathan Doman             GetCoreMask(pm, static_cast<uint8_t>(level), 0).coresMask();
469*16a2ced3SJonathan Doman         uint64_t coreMaskHi =
470*16a2ced3SJonathan Doman             GetCoreMask(pm, static_cast<uint8_t>(level), 1).coresMask();
471*16a2ced3SJonathan Doman         std::bitset<64> coreMask = (coreMaskHi << 32 | coreMaskLo);
472*16a2ced3SJonathan Doman         return convertMaskToList(coreMask);
473*16a2ced3SJonathan Doman     }
474*16a2ced3SJonathan Doman     std::vector<TurboEntry> sseTurboProfile(unsigned int level) override
475*16a2ced3SJonathan Doman     {
476*16a2ced3SJonathan Doman         // Read the Turbo Ratio Limit Cores MSR which is used to generate the
477*16a2ced3SJonathan Doman         // Turbo Profile for each profile. This is a package scope MSR, so just
478*16a2ced3SJonathan Doman         // read thread 0.
479*16a2ced3SJonathan Doman         uint64_t trlCores;
480*16a2ced3SJonathan Doman         uint8_t cc;
481*16a2ced3SJonathan Doman         EPECIStatus status = peci_RdIAMSR(static_cast<uint8_t>(address), 0,
482*16a2ced3SJonathan Doman                                           0x1AE, &trlCores, &cc);
483*16a2ced3SJonathan Doman         if (!checkPECIStatus(status, cc))
484*16a2ced3SJonathan Doman         {
485*16a2ced3SJonathan Doman             throw PECIError("Failed to read TRL MSR");
486*16a2ced3SJonathan Doman         }
487*16a2ced3SJonathan Doman 
488*16a2ced3SJonathan Doman         std::vector<TurboEntry> turboSpeeds;
489*16a2ced3SJonathan Doman         uint64_t limitRatioLo =
490*16a2ced3SJonathan Doman             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 0, 0).value;
491*16a2ced3SJonathan Doman         uint64_t limitRatioHi =
492*16a2ced3SJonathan Doman             GetTurboLimitRatios(pm, static_cast<uint8_t>(level), 1, 0).value;
493*16a2ced3SJonathan Doman         uint64_t limitRatios = (limitRatioHi << 32) | limitRatioLo;
494*16a2ced3SJonathan Doman 
495*16a2ced3SJonathan Doman         constexpr int maxTFBuckets = 8;
496*16a2ced3SJonathan Doman         for (int i = 0; i < maxTFBuckets; ++i)
497*16a2ced3SJonathan Doman         {
498*16a2ced3SJonathan Doman             size_t bucketCount = trlCores & 0xFF;
499*16a2ced3SJonathan Doman             int bucketSpeed = limitRatios & 0xFF;
500*16a2ced3SJonathan Doman             if (bucketCount != 0 && bucketSpeed != 0)
501*16a2ced3SJonathan Doman             {
502*16a2ced3SJonathan Doman                 turboSpeeds.push_back({bucketSpeed * mhzPerRatio, bucketCount});
503*16a2ced3SJonathan Doman             }
504*16a2ced3SJonathan Doman 
505*16a2ced3SJonathan Doman             trlCores >>= 8;
506*16a2ced3SJonathan Doman             limitRatios >>= 8;
507*16a2ced3SJonathan Doman         }
508*16a2ced3SJonathan Doman         return turboSpeeds;
509*16a2ced3SJonathan Doman     }
510*16a2ced3SJonathan Doman     unsigned int p1Freq(unsigned int level) override
511*16a2ced3SJonathan Doman     {
512*16a2ced3SJonathan Doman         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p1() * mhzPerRatio;
513*16a2ced3SJonathan Doman     }
514*16a2ced3SJonathan Doman     unsigned int p0Freq(unsigned int level) override
515*16a2ced3SJonathan Doman     {
516*16a2ced3SJonathan Doman         return GetRatioInfo(pm, static_cast<uint8_t>(level)).p0() * mhzPerRatio;
517*16a2ced3SJonathan Doman     }
518*16a2ced3SJonathan Doman     unsigned int prochotTemp(unsigned int level) override
519*16a2ced3SJonathan Doman     {
520*16a2ced3SJonathan Doman         return GetTjmaxInfo(pm, static_cast<uint8_t>(level)).tProchot();
521*16a2ced3SJonathan Doman     }
522*16a2ced3SJonathan Doman     std::vector<unsigned int>
523*16a2ced3SJonathan Doman         bfHighPriorityCoreList(unsigned int level) override
524*16a2ced3SJonathan Doman     {
525*16a2ced3SJonathan Doman         uint64_t coreMaskLo =
526*16a2ced3SJonathan Doman             PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 0)
527*16a2ced3SJonathan Doman                 .p1HiCoreMask();
528*16a2ced3SJonathan Doman         uint64_t coreMaskHi =
529*16a2ced3SJonathan Doman             PbfGetCoreMaskInfo(pm, static_cast<uint8_t>(level), 1)
530*16a2ced3SJonathan Doman                 .p1HiCoreMask();
531*16a2ced3SJonathan Doman         std::bitset<64> hiFreqCoreList = (coreMaskHi << 32) | coreMaskLo;
532*16a2ced3SJonathan Doman         return convertMaskToList(hiFreqCoreList);
533*16a2ced3SJonathan Doman     }
534*16a2ced3SJonathan Doman     unsigned int bfHighPriorityFreq(unsigned int level) override
535*16a2ced3SJonathan Doman     {
536*16a2ced3SJonathan Doman         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Hi() *
537*16a2ced3SJonathan Doman                mhzPerRatio;
538*16a2ced3SJonathan Doman     }
539*16a2ced3SJonathan Doman     unsigned int bfLowPriorityFreq(unsigned int level) override
540*16a2ced3SJonathan Doman     {
541*16a2ced3SJonathan Doman         return PbfGetP1HiP1LoInfo(pm, static_cast<uint8_t>(level)).p1Lo() *
542*16a2ced3SJonathan Doman                mhzPerRatio;
543*16a2ced3SJonathan Doman     }
544*16a2ced3SJonathan Doman 
545*16a2ced3SJonathan Doman     void setBfEnabled(bool enable) override
546*16a2ced3SJonathan Doman     {
547*16a2ced3SJonathan Doman         GetConfigTdpControl getTDPControl(pm);
548*16a2ced3SJonathan Doman         bool tfEnabled = false;
549*16a2ced3SJonathan Doman         uint8_t param = (enable ? bit(1) : 0) | (tfEnabled ? bit(0) : 0);
550*16a2ced3SJonathan Doman         SetConfigTdpControl(pm, 0, 0, param);
551*16a2ced3SJonathan Doman     }
552*16a2ced3SJonathan Doman     void setTfEnabled(bool enable) override
553*16a2ced3SJonathan Doman     {
554*16a2ced3SJonathan Doman         // TODO: use cached BF value
555*16a2ced3SJonathan Doman         bool bfEnabled = false;
556*16a2ced3SJonathan Doman         uint8_t param = (bfEnabled ? bit(1) : 0) | (enable ? bit(0) : 0);
557*16a2ced3SJonathan Doman         SetConfigTdpControl(pm, 0, 0, param);
558*16a2ced3SJonathan Doman     }
559*16a2ced3SJonathan Doman     void setCurrentLevel(unsigned int level) override
560*16a2ced3SJonathan Doman     {
561*16a2ced3SJonathan Doman         SetLevel(pm, static_cast<uint8_t>(level));
562*16a2ced3SJonathan Doman     }
563*16a2ced3SJonathan Doman };
564*16a2ced3SJonathan Doman 
565*16a2ced3SJonathan Doman static std::unique_ptr<SSTInterface> createMailbox(uint8_t address,
566*16a2ced3SJonathan Doman                                                    CPUModel model)
567*16a2ced3SJonathan Doman {
568*16a2ced3SJonathan Doman     DEBUG_PRINT << "createMailbox\n";
569*16a2ced3SJonathan Doman     if (model == icx || model == icxd || model == spr)
570*16a2ced3SJonathan Doman     {
571*16a2ced3SJonathan Doman         return std::make_unique<SSTMailbox>(address, model);
572*16a2ced3SJonathan Doman     }
573*16a2ced3SJonathan Doman 
574*16a2ced3SJonathan Doman     return nullptr;
575*16a2ced3SJonathan Doman }
576*16a2ced3SJonathan Doman 
577*16a2ced3SJonathan Doman SSTProviderRegistration(createMailbox);
578*16a2ced3SJonathan Doman 
579*16a2ced3SJonathan Doman } // namespace sst
580*16a2ced3SJonathan Doman } // namespace cpu_info
581